<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"testing"
)

var exampleProgramTests = []struct {
	programfile string // Example program file.
	logfile     string // Sample log input.
	jsonfile    string // Expected metrics after processing.
}{
	{
		"examples/linecount.em",
		"testdata/linecount.log",
		"testdata/linecount.json",
	},
	{
		"examples/rsyncd.em",
		"testdata/rsyncd.log",
		"testdata/rsyncd.json",
	},
}

func CompileAndLoad(programfile string) (chan string, string) {
	metrics = make(map[string][]*Metric, 0)
	lines := make(chan string)

	p, err := os.Open(programfile)
	if err != nil {
		return lines, fmt.Sprintf("%s: could not open program file: %s", programfile, err)
	}
	defer p.Close()

	// EmtailDebug = 999 // All the debugging.

	v, errs := Compile(programfile, p)
	if errs != nil {
		return lines, fmt.Sprintf("%s: compile failed: %s", programfile, strings.Join(errs, "\n"))
	}

	vms = make([]*vm, 0)
	vms = append(vms, v)

	go RunVms(vms, lines)

	return lines, ""
}

func TestExamplePrograms(t *testing.T) {
	if testing.Short() {
		return
	}
TestLoop:
	for _, tc := range exampleProgramTests {
		lines, errs := CompileAndLoad(tc.programfile)
		if errs != "" {
			t.Errorf(errs)
			continue TestLoop
		}

		err := OneShot(tc.logfile, lines)
		if err != nil {
			t.Errorf("Oneshot failed: %s", err)
			continue TestLoop
		}

		// // Dirty hack to create json files :)
		// {
		// 	j, err := os.Create(tc.jsonfile)
		// 	if err != nil {
		// 		t.Errorf("%s: could not open json file: %s", tc.jsonfile, err)
		// 		continue
		// 	}
		// 	defer j.Close()
		// 	b, err := json.MarshalIndent(metrics, "", "  ")
		// 	if err != nil {
		// 		t.Errorf("couldn't marshall metrics")
		// 		continue
		// 	}
		// 	j.Write(b)
		// }

		j, err := os.Open(tc.jsonfile)
		if err != nil {
			t.Errorf("%s: could not open json file: %s", tc.jsonfile, err)
			continue
		}
		defer j.Close()

		var expected_metrics []*Metric

		d := json.NewDecoder(j)
		err = d.Decode(&expected_metrics)
		if err != nil {
			t.Errorf("%s: could not decode json: %s", tc.jsonfile, err)
			continue
		}
		name := filepath.Base(tc.programfile)
		if strings.HasSuffix(name, ".em") {
			name = name[:len(name)-3]
		}

		exported_metrics := make([]*Metric, 0)
		for _, m := range metrics[name] {
			if !m.hidden {
				exported_metrics = append(exported_metrics, m)
			}
		}

		if !reflect.DeepEqual(expected_metrics, exported_metrics) {
			t.Errorf("%s: metrics don't match.\n\texpected: %s\n\treceived: %s", tc.programfile, expected_metrics, exported_metrics)
		}
	}
}

func runProgramBenchmark(programfile string, logfile string, b *testing.B) {
	b.StopTimer()
	lines, errs := CompileAndLoad(programfile)
	if errs != "" {
		b.Errorf(errs)
		return
	}
	for run := 0; run < b.N; run++ {
		b.StartTimer()
		err := OneShot(logfile, lines)
		if err != nil {
			b.Errorf("Oneshot failed: %s", err)
			return
		}
		b.StopTimer()
		i, err := strconv.ParseInt(line_count.String(), 10, 64)
		if err != nil {
			b.Errorf("strconv failed: %s", err)
		}
		b.SetBytes(i)
	}
}

// These benchmarks run the testdata logs through the example programs.
// Caveat emptor:
// The ns/op measure returned is the time spent on a OneShot for a single program.
// The MB/s measure should be interpreted as megalines processed per second.

func BenchmarkExampleLineCount(b *testing.B) {
	runProgramBenchmark("examples/linecount.em", "testdata/linecount.log", b)
}

func BenchmarkExampleRsyncd(b *testing.B) {
	runProgramBenchmark("examples/rsyncd.em", "testdata/rsyncd.log", b)
}<commit_msg>Hacked up benchmark for example programs.<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package main

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"strings"
	"testing"
)

var exampleProgramTests = []struct {
	programfile string // Example program file.
	logfile     string // Sample log input.
	jsonfile    string // Expected metrics after processing.
}{
	{
		"examples/linecount.em",
		"testdata/linecount.log",
		"testdata/linecount.json",
	},
	{
		"examples/rsyncd.em",
		"testdata/rsyncd.log",
		"testdata/rsyncd.json",
	},
}

func CompileAndLoad(programfile string) (chan string, string) {
	metrics = make(map[string][]*Metric, 0)
	lines := make(chan string)

	p, err := os.Open(programfile)
	if err != nil {
		return lines, fmt.Sprintf("%s: could not open program file: %s", programfile, err)
	}
	defer p.Close()

	// EmtailDebug = 999 // All the debugging.

	v, errs := Compile(programfile, p)
	if errs != nil {
		return lines, fmt.Sprintf("%s: compile failed: %s", programfile, strings.Join(errs, "\n"))
	}

	vms = make([]*vm, 0)
	vms = append(vms, v)

	go RunVms(vms, lines)

	return lines, ""
}

func TestExamplePrograms(t *testing.T) {
	if testing.Short() {
		return
	}
TestLoop:
	for _, tc := range exampleProgramTests {
		lines, errs := CompileAndLoad(tc.programfile)
		if errs != "" {
			t.Errorf(errs)
			continue TestLoop
		}

		err := OneShot(tc.logfile, lines)
		if err != nil {
			t.Errorf("Oneshot failed: %s", err)
			continue TestLoop
		}

		// // Dirty hack to create json files :)
		// {
		// 	j, err := os.Create(tc.jsonfile)
		// 	if err != nil {
		// 		t.Errorf("%s: could not open json file: %s", tc.jsonfile, err)
		// 		continue
		// 	}
		// 	defer j.Close()
		// 	b, err := json.MarshalIndent(metrics, "", "  ")
		// 	if err != nil {
		// 		t.Errorf("couldn't marshall metrics")
		// 		continue
		// 	}
		// 	j.Write(b)
		// }

		j, err := os.Open(tc.jsonfile)
		if err != nil {
			t.Errorf("%s: could not open json file: %s", tc.jsonfile, err)
			continue
		}
		defer j.Close()

		var expected_metrics []*Metric

		d := json.NewDecoder(j)
		err = d.Decode(&expected_metrics)
		if err != nil {
			t.Errorf("%s: could not decode json: %s", tc.jsonfile, err)
			continue
		}
		name := filepath.Base(tc.programfile)
		if strings.HasSuffix(name, ".em") {
			name = name[:len(name)-3]
		}

		exported_metrics := make([]*Metric, 0)
		for _, m := range metrics[name] {
			if !m.hidden {
				exported_metrics = append(exported_metrics, m)
			}
		}

		if !reflect.DeepEqual(expected_metrics, exported_metrics) {
			t.Errorf("%s: metrics don't match.\n\texpected: %s\n\treceived: %s", tc.programfile, expected_metrics, exported_metrics)
		}
	}
}

func runProgramBenchmark(programfile string, logfile string, b *testing.B) {
	b.StopTimer()
	lines, errs := CompileAndLoad(programfile)
	if errs != "" {
		b.Errorf(errs)
		return
	}
	for run := 0; run < b.N; run++ {
		b.StartTimer()
		err := OneShot(logfile, lines)
		if err != nil {
			b.Errorf("Oneshot failed: %s", err)
			return
		}
		b.StopTimer()
		i, err := strconv.ParseInt(line_count.String(), 10, 64)
		if err != nil {
			b.Errorf("strconv failed: %s", err)
		}
		b.SetBytes(i)
	}
}

// These benchmarks run the testdata logs through the example programs.
// Caveat emptor:
// The ns/op measure returned is the time spent on a OneShot for a single program.
// The MB/s measure should be interpreted as megalines processed per second.
func BenchmarkExamplePrograms(b *testing.B) {
	b.StopTimer()
	for _, tc := range exampleProgramTests {
		result := testing.Benchmark(func(b *testing.B) {
			// for i := 0; i < b.N; i++ {
			runProgramBenchmark(tc.programfile, tc.logfile, b)
			// }
		})
		fmt.Printf("%s took %d %s (%d ns/run, %f lines/s)\n", tc.programfile, result.N, result.T, result.NsPerOp(), (float64(result.Bytes)*float64(result.N))/float64(result.T.Seconds()))
	}
}

// func BenchmarkExampleLineCount(b *testing.B) {
// 	runProgramBenchmark("examples/linecount.em", "testdata/linecount.log", b)
// }

// func BenchmarkExampleRsyncd(b *testing.B) {
// 	runProgramBenchmark("examples/rsyncd.em", "testdata/rsyncd.log", b)
// }
// 	for
{'fraction_non_alphanumeric': 0.15832494969818914, 'fraction_numerical': 0.004904426559356137, 'mean_word_length': 3.2758064516129033, 'pattern_counts': {'":': 0, '<': 6, '<?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': '6902382', 'n_tokens_mistral': 3226, 'n_tokens_neox': 2948, 'n_words': 731}