<commit_before>package speed

import (
	"fmt"
	"os"
	"testing"

	"github.com/performancecopilot/speed/mmvdump"
)

func TestMmvFileLocation(t *testing.T) {
	l, present := config["PCP_TMP_DIR"]

	if present {
		loc, _ := mmvFileLocation("test")
		expected := fmt.Sprintf("%v%cmmv%c%v", l, os.PathSeparator, os.PathSeparator, "test")
		if loc != expected {
			t.Errorf("location not expected value, expected %v, got %v", expected, loc)
		}
	}

	delete(config, "PCP_TMP_DIR")
	loc, _ := mmvFileLocation("test")
	expected := fmt.Sprintf("%v%cmmv%c%v", os.TempDir(), os.PathSeparator, os.PathSeparator, "test")
	if loc != expected {
		t.Errorf("location not expected value, expected %v, got %v", expected, loc)
	}

	if present {
		config["PCP_TMP_DIR"] = l
	}

	loc, err := mmvFileLocation(fmt.Sprintf("%v%c", "test", os.PathSeparator))
	if err == nil {
		t.Errorf("expected error, instead got path %v", loc)
	}
}

func TestTocCountAndLength(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	if err != nil {
		t.Errorf("cannot create writer, error: %v", err)
	}

	if c.tocCount() != 2 {
		t.Errorf("expected tocCount to be 2, got %v", c.tocCount())
	}

	expectedLength := HeaderLength + 2*TocLength
	if c.Length() != expectedLength {
		t.Errorf("expected Length to be %v, got %v", expectedLength, c.Length())
	}

	m, err := NewPCPSingletonMetric(10, "test", Int32Type, CounterSemantics, OneUnit, "test", "")
	if err != nil {
		t.Error("Cannot create a metric")
	}

	c.MustRegister(m)
	if c.tocCount() != 3 {
		t.Errorf("expected tocCount to be 3, got %v", c.tocCount())
	}

	expectedLength = HeaderLength + 3*TocLength + 1*MetricLength + 1*ValueLength + 1*StringLength
	if c.Length() != expectedLength {
		t.Errorf("expected Length to be %v, got %v", expectedLength, c.Length())
	}

	indom, _ := NewPCPInstanceDomain("testindom", []string{"test"}, "", "")
	c.MustRegisterIndom(indom)

	m2, err := NewPCPInstanceMetric(
		Instances{
			"test": 1,
		},
		"test2", indom, Int32Type, CounterSemantics, OneUnit, "", "",
	)
	if err != nil {
		t.Error("Cannot create a metric")
	}

	err = c.Register(m2)
	if err != nil {
		t.Error("Cannot register m2")
	}

	if c.tocCount() != 5 {
		t.Errorf("expected tocCount to be 5, got %v", c.tocCount())
	}
}

func TestMapping(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	_, err = c.RegisterString("test.1", 2, CounterSemantics, Int32Type, OneUnit)
	if err != nil {
		t.Error("Cannot Register")
	}

	c.MustStart()
	loc, _ := mmvFileLocation("test")
	if _, err = os.Stat(loc); err != nil {
		t.Error("expected a MMV file to be created on startup")
	}

	_, err = c.RegisterString("test.2", 2, CounterSemantics, Int32Type, OneUnit)
	if err == nil {
		t.Error("expected registration to fail when a mapping is active")
	}

	EraseFileOnStop = true
	err = c.Stop()
	if err != nil {
		t.Error("Cannot stop a mapping")
	}

	if _, err = os.Stat(loc); err == nil {
		t.Error("expected the MMV file be deleted after stopping")
	}

	EraseFileOnStop = false
}

func TestWritingSingletonMetric(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	if err != nil {
		t.Error(err)
		return
	}

	met, err := NewPCPSingletonMetric(10, "test.1", Int32Type, CounterSemantics, OneUnit, "test", "")
	if err != nil {
		t.Error(err)
		return
	}
	c.MustRegister(met)

	c.MustStart()
	defer c.MustStop()

	h, toc, m, v, i, ind, s, err := mmvdump.Dump(c.buffer.Bytes())

	if int(h.Toc) != len(toc) {
		t.Errorf("expected the number of tocs specified in the header and the number of tocs in the toc array to be the same, h.Toc = %d, len(tocs) = %d", h.Toc, len(toc))
	}

	if h.Toc != 3 {
		t.Errorf("expected client to write %d tocs, written %d", 3, h.Toc)
	}

	if h.Flag != int32(ProcessFlag) {
		t.Errorf("expected client to write a ProcessFlag, writing %v", MMVFlag(h.Flag))
	}

	if len(m) != 1 {
		t.Errorf("expected to write %d metrics, writing %d", 1, len(m))
	}

	// metrics

	off := c.r.metricsoffset
	metric, ok := m[uint64(off)]
	if !ok {
		t.Errorf("expected the metric to exist at offset %v", off)
	}

	if metric.Indom != mmvdump.NoIndom {
		t.Error("expected indom to be null")
	}

	if int32(metric.Sem) != int32(CounterSemantics) {
		t.Errorf("expected semantics to be %v, got %v", CounterSemantics, MetricSemantics(metric.Sem))
	}

	if int32(metric.Typ) != int32(Int32Type) {
		t.Errorf("expected type to be %v, got %v", Int32Type, MetricType(metric.Typ))
	}

	if int32(metric.Unit) != int32(OneUnit) {
		t.Errorf("expected unit to be %v, got %v", OneUnit, metric.Unit)
	}

	if metric.Shorttext == 0 {
		t.Error("expected shorttext offset to not be 0")
	}

	if metric.Shorttext != uint64(c.r.stringsoffset) {
		t.Errorf("expected shorttext offset to be %v", c.r.stringsoffset)
	}

	if metric.Longtext != 0 {
		t.Errorf("expected longtext offset to be 0")
	}

	// values

	mv, ok := v[uint64(c.r.valuesoffset)]
	if !ok {
		t.Errorf("expected a value to be written at offset %v", c.r.valuesoffset)
	}

	if mv.Metric != uint64(off) {
		t.Errorf("expected value's metric to be at %v", off)
	}

	if av, err := mmvdump.FixedVal(mv.Val, mmvdump.Int32Type); err != nil || av.(int32) != 10 {
		t.Errorf("expected the value to be %v, got %v", 10, av)
	}

	if mv.Instance != 0 {
		t.Errorf("expected value instance to be 0")
	}

	// strings

	if len(s) != 1 {
		t.Error("expected one string")
	}

	str, ok := s[uint64(c.r.stringsoffset)]
	if !ok {
		t.Errorf("expected a string to be written at offset %v", c.r.stringsoffset)
	}

	sv := string(str.Payload[:4])
	if sv != "test" {
		t.Errorf("expected payload to be %v, got %v", "test", sv)
	}

	// instances

	if len(i) != 0 {
		t.Error("expected no instances when writing a singleton metric")
	}

	// indoms

	if len(ind) != 0 {
		t.Error("expected no indoms when writing a singleton metric")
	}
}<commit_msg>client: add test for testing writing instance metrics<commit_after>package speed

import (
	"fmt"
	"os"
	"testing"

	"github.com/performancecopilot/speed/mmvdump"
)

func TestMmvFileLocation(t *testing.T) {
	l, present := config["PCP_TMP_DIR"]

	if present {
		loc, _ := mmvFileLocation("test")
		expected := fmt.Sprintf("%v%cmmv%c%v", l, os.PathSeparator, os.PathSeparator, "test")
		if loc != expected {
			t.Errorf("location not expected value, expected %v, got %v", expected, loc)
		}
	}

	delete(config, "PCP_TMP_DIR")
	loc, _ := mmvFileLocation("test")
	expected := fmt.Sprintf("%v%cmmv%c%v", os.TempDir(), os.PathSeparator, os.PathSeparator, "test")
	if loc != expected {
		t.Errorf("location not expected value, expected %v, got %v", expected, loc)
	}

	if present {
		config["PCP_TMP_DIR"] = l
	}

	loc, err := mmvFileLocation(fmt.Sprintf("%v%c", "test", os.PathSeparator))
	if err == nil {
		t.Errorf("expected error, instead got path %v", loc)
	}
}

func TestTocCountAndLength(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	if err != nil {
		t.Errorf("cannot create writer, error: %v", err)
	}

	if c.tocCount() != 2 {
		t.Errorf("expected tocCount to be 2, got %v", c.tocCount())
	}

	expectedLength := HeaderLength + 2*TocLength
	if c.Length() != expectedLength {
		t.Errorf("expected Length to be %v, got %v", expectedLength, c.Length())
	}

	m, err := NewPCPSingletonMetric(10, "test", Int32Type, CounterSemantics, OneUnit, "test", "")
	if err != nil {
		t.Error("Cannot create a metric")
	}

	c.MustRegister(m)
	if c.tocCount() != 3 {
		t.Errorf("expected tocCount to be 3, got %v", c.tocCount())
	}

	expectedLength = HeaderLength + 3*TocLength + 1*MetricLength + 1*ValueLength + 1*StringLength
	if c.Length() != expectedLength {
		t.Errorf("expected Length to be %v, got %v", expectedLength, c.Length())
	}

	indom, _ := NewPCPInstanceDomain("testindom", []string{"test"}, "", "")
	c.MustRegisterIndom(indom)

	m2, err := NewPCPInstanceMetric(
		Instances{
			"test": 1,
		},
		"test2", indom, Int32Type, CounterSemantics, OneUnit, "", "",
	)
	if err != nil {
		t.Error("Cannot create a metric")
	}

	err = c.Register(m2)
	if err != nil {
		t.Error("Cannot register m2")
	}

	if c.tocCount() != 5 {
		t.Errorf("expected tocCount to be 5, got %v", c.tocCount())
	}
}

func TestMapping(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	_, err = c.RegisterString("test.1", 2, CounterSemantics, Int32Type, OneUnit)
	if err != nil {
		t.Error("Cannot Register")
	}

	c.MustStart()
	loc, _ := mmvFileLocation("test")
	if _, err = os.Stat(loc); err != nil {
		t.Error("expected a MMV file to be created on startup")
	}

	_, err = c.RegisterString("test.2", 2, CounterSemantics, Int32Type, OneUnit)
	if err == nil {
		t.Error("expected registration to fail when a mapping is active")
	}

	EraseFileOnStop = true
	err = c.Stop()
	if err != nil {
		t.Error("Cannot stop a mapping")
	}

	if _, err = os.Stat(loc); err == nil {
		t.Error("expected the MMV file be deleted after stopping")
	}

	EraseFileOnStop = false
}

func matchSingletonMetric(m *PCPSingletonMetric, metric *mmvdump.Metric, t *testing.T) {
	if metric.Indom != mmvdump.NoIndom {
		t.Error("expected indom to be null")
	}

	if int32(metric.Sem) != int32(m.sem) {
		t.Errorf("expected semantics to be %v, got %v", m.sem, MetricSemantics(metric.Sem))
	}

	if int32(metric.Typ) != int32(m.t) {
		t.Errorf("expected type to be %v, got %v", m.t, MetricType(metric.Typ))
	}

	if int32(metric.Unit) != int32(m.u.PMAPI()) {
		t.Errorf("expected unit to be %v, got %v", m.u, metric.Unit)
	}

	if metric.Shorttext != uint64(m.shortDescription.offset) {
		t.Errorf("expected shorttext to be %v, got %v", m.shortDescription.offset, metric.Shorttext)
	}

	if metric.Longtext != uint64(m.longDescription.offset) {
		t.Errorf("expected longtext to be %v, got %v", m.longDescription.offset, metric.Longtext)
	}
}

func matchSingletonValue(m *PCPSingletonMetric, value *mmvdump.Value, t *testing.T) {
	if value.Metric != uint64(m.descoffset) {
		t.Errorf("expected value's metric to be at %v", m.descoffset)
	}

	if av, err := mmvdump.FixedVal(value.Val, mmvdump.Int32Type); err != nil || av.(int32) != m.val.(int32) {
		t.Errorf("expected the value to be %v, got %v", 10, av)
	}

	if value.Instance != 0 {
		t.Errorf("expected value instance to be 0")
	}
}

func matchString(s *PCPString, str *mmvdump.String, t *testing.T) {
	if s == nil {
		t.Error("expected PCPString to not be nil")
	}
	sv := string(str.Payload[:len(s.val)])
	if sv != s.val {
		t.Errorf("expected %v, got %v", s.val, sv)
	}
}

func matchMetricsAndValues(metrics map[uint64]*mmvdump.Metric, values map[uint64]*mmvdump.Value, c *PCPClient, t *testing.T) {
	if c.Registry().MetricCount() != len(metrics) {
		t.Errorf("expected %v metrics, got %v", c.Registry().MetricCount(), len(metrics))
	}

	if c.Registry().ValuesCount() != len(values) {
		t.Errorf("expected %v values, got %v", c.Registry().ValuesCount(), len(values))
	}

	for _, m := range c.r.metrics {
		switch met := m.(type) {
		case *PCPSingletonMetric:
			metric, ok := metrics[uint64(met.descoffset)]
			if !ok {
				t.Errorf("expected a metric at offset %v", met.descoffset)
				continue
			}
			matchSingletonMetric(met, metric, t)

			mv, ok := values[uint64(met.valueoffset)]
			if !ok {
				t.Errorf("expected a value at offset %v", met.valueoffset)
				continue
			}
			matchSingletonValue(met, mv, t)
		}
	}
}

func TestWritingSingletonMetric(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	if err != nil {
		t.Error(err)
		return
	}

	met, err := NewPCPSingletonMetric(10, "test.1", Int32Type, CounterSemantics, OneUnit, "test", "")
	if err != nil {
		t.Error(err)
		return
	}
	c.MustRegister(met)

	c.MustStart()
	defer c.MustStop()

	h, toc, m, v, i, ind, s, err := mmvdump.Dump(c.buffer.Bytes())
	if err != nil {
		t.Error(err)
		return
	}

	if int(h.Toc) != len(toc) {
		t.Errorf("expected the number of tocs specified in the header and the number of tocs in the toc array to be the same, h.Toc = %d, len(tocs) = %d", h.Toc, len(toc))
	}

	if h.Toc != 3 {
		t.Errorf("expected client to write %d tocs, written %d", 3, h.Toc)
	}

	if h.Flag != int32(ProcessFlag) {
		t.Errorf("expected client to write a ProcessFlag, writing %v", MMVFlag(h.Flag))
	}

	matchMetricsAndValues(m, v, c, t)

	// strings

	if len(s) != 1 {
		t.Error("expected one string")
	}

	matchString(met.shortDescription, s[uint64(met.shortDescription.offset)], t)

	// instances

	if len(i) != 0 {
		t.Error("expected no instances when writing a singleton metric")
	}

	// indoms

	if len(ind) != 0 {
		t.Error("expected no indoms when writing a singleton metric")
	}
}

func matchInstance(i *mmvdump.Instance, pi *pcpInstance, id *PCPInstanceDomain, t *testing.T) {
	if i.Indom != uint64(id.offset) {
		t.Errorf("expected indom offset to be %d, got %d", i.Indom, id.offset)
	}

	if in := i.External[:len(pi.name)]; pi.name != string(in) {
		t.Errorf("expected instance name to be %v, got %v", pi.name, in)
	}
}

func matchInstanceDomain(id *mmvdump.InstanceDomain, pid *PCPInstanceDomain, t *testing.T) {
	if pid.InstanceCount() != int(id.Count) {
		t.Errorf("expected %d instances in instance domain %v, got %d", pid.InstanceCount(), pid.Name(), id.Count)
	}

	if id.Offset != uint64(pid.instanceOffset) {
		t.Errorf("expected instance offset to be %d, got %d", pid.instanceOffset, id.Offset)
	}

	if id.Shorttext != uint64(pid.shortDescription.offset) {
		t.Errorf("expected short description to be %d, got %d", pid.shortDescription.offset, id.Shorttext)
	}

	if id.Longtext != uint64(pid.longDescription.offset) {
		t.Errorf("expected long description to be %d, got %d", pid.longDescription.offset, id.Longtext)
	}
}

func matchInstancesAndInstanceDomains(
	ins map[uint64]*mmvdump.Instance,
	ids map[uint64]*mmvdump.InstanceDomain,
	c *PCPClient,
	t *testing.T,
) {
	if len(ins) != c.r.InstanceCount() {
		t.Errorf("expected %d instances, got %d", c.r.InstanceCount(), len(ins))
	}

	for _, id := range c.r.instanceDomains {
		if off := uint64(id.offset); ids[off] == nil {
			t.Errorf("expected an instance domain at %d", id.offset)
		} else {
			matchInstanceDomain(ids[off], id, t)
		}

		for _, i := range id.instances {
			if ioff := uint64(i.offset); ins[ioff] == nil {
				t.Errorf("expected an instance domain at %d", ioff)
			} else {
				matchInstance(ins[ioff], i, id, t)
			}
		}
	}
}

func TestWritingInstanceMetric(t *testing.T) {
	c, err := NewPCPClient("test", ProcessFlag)
	if err != nil {
		t.Error(err)
		return
	}

	id, err := NewPCPInstanceDomain("testid", []string{"a", "b", "c"}, "testid", "")
	if err != nil {
		t.Error(err)
		return
	}
	c.MustRegisterIndom(id)

	m, err := NewPCPInstanceMetric(Instances{
		"a": 1,
		"b": 2,
		"c": 3,
	}, "test.1", id, Uint32Type, CounterSemantics, OneUnit, "", "test long description")
	if err != nil {
		t.Error(err)
		return
	}

	c.MustRegister(m)

	c.MustStart()
	defer c.MustStop()

	h, tocs, mets, vals, ins, ids, ss, err := mmvdump.Dump(c.buffer.Bytes())
	if err != nil {
		t.Error(err)
		return
	}

	if int(h.Toc) != len(tocs) {
		t.Errorf("expected the number of tocs specified in the header and the number of tocs in the toc array to be the same, h.Toc = %d, len(tocs) = %d", h.Toc, len(tocs))
	}

	if h.Toc != 5 {
		t.Errorf("expected client to write %d tocs, written %d", 5, h.Toc)
	}

	if h.Flag != int32(ProcessFlag) {
		t.Errorf("expected client to write a ProcessFlag, writing %v", MMVFlag(h.Flag))
	}

	matchMetricsAndValues(mets, vals, c, t)

	matchInstancesAndInstanceDomains(ins, ids, c, t)

	// strings

	if off := id.shortDescription.offset; ss[uint64(off)] != nil {
		matchString(id.shortDescription, ss[uint64(off)], t)
	} else {
		t.Errorf("expected a string at offset %v", off)
	}

	if off := m.longDescription.offset; ss[uint64(off)] != nil {
		matchString(m.longDescription, ss[uint64(off)], t)
	} else {
		t.Errorf("expected a string at offset %v", off)
	}
}
{'fraction_non_alphanumeric': 0.16464691326692468, 'fraction_numerical': 0.012626102404669754, 'mean_word_length': 3.9102803738317755, 'pattern_counts': {'":': 5, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1013691', 'n_tokens_mistral': 6395, 'n_tokens_neox': 5916, 'n_words': 1564}