<commit_before>package strinterp

import (
	"bytes"
	"errors"
	"fmt"
	"html"
	"io"
	"reflect"
	"testing"
)

type StrescTest struct {
	Format string
	Args   []interface{}
	Result string
	Error  error
}

func TestDefaultInterpolator(t *testing.T) {
	noargs := []interface{}{}

	tests := []StrescTest{
		{"x", noargs, "x", nil},
		{"x%%;y", []interface{}{}, "x%y", nil},
		{"x%RAW;", []interface{}{"y"}, "xy", nil},
		{"x%RAW;", []interface{}{[]byte("y")}, "xy", nil},
		{"x%RAW;", []interface{}{bytes.NewBuffer([]byte("y"))}, "xy", nil},

		{"x%RAW", []interface{}{"y"}, "", ErrIncompleteFormatString},
		{"x%RAW;", []interface{}{0}, "", ErrRawUnknownType},
		{"x%RAW;", []interface{}{}, "x%RAW error: No arg;", nil},
		{"x%blargh;", []interface{}{}, "", ErrUnknownFormatter("blargh")},
	}

	i := NewInterpolator()

	for _, test := range tests {
		res, err := i.InterpStr(test.Format, test.Args...)

		if test.Error != nil && !reflect.DeepEqual(test.Error, err) {
			// note in this case we aren't being hypocritcal... having just
			// established this package's interpolation is actually broken,
			// don't try to use it to output an error message!
			t.Fatal(fmt.Sprintf("for %s, expected error %v, got %v", test.Format, test.Error, err))
		}
		if test.Result != "" && test.Result != res {
			t.Fatal(fmt.Sprintf("for %s, expected result '%s', got '%s'", test.Format, test.Result, res))
		}
	}
}

func TestOtherInterpolators(t *testing.T) {
	i := NewInterpolator()

	err := i.AddFormat("cdata", func(w io.Writer, arg interface{}, params []byte) error {
		switch a := arg.(type) {
		case []byte:
			newBytes := []byte(html.EscapeString(string(a)))
			_, err := w.Write(newBytes)
			return err
		case string:
			newBytes := []byte(html.EscapeString(a))
			_, err := w.Write(newBytes)
			return err
		default:
			return errors.New("unknown type for cdata")
		}
	})

	if err != nil {
		t.Fatal("Can't add new format?")
	}

	err = i.AddFormat("cdata", func(io.Writer, interface{}, []byte) error { return nil })
	if !reflect.DeepEqual(err, ErrAlreadyExists("cdata")) {
		t.Fatal("fails to catch double-type add")
	}

	res, err := i.InterpStr("hello %cdata;", "& stuff")
	if err != nil {
		t.Fatal("Error: " + err.Error())
	}

	if res != "hello &amp; stuff" {
		t.Fatal("Didn't get correct cdata string: " + res)
	}
}

func TestWriterErrors(t *testing.T) {
	i := NewInterpolator()

	err := i.InterpWriter(WriterAlwaysEOF{}, []byte("x"))
	if err != io.EOF {
		t.Fatal("Don't handle EOF in writer correctly")
	}

	err = i.InterpWriter(WriterAlwaysEOF{}, []byte("x%RAW;"), "x")
	if err != io.EOF {
		t.Fatal("Doesn't handle EOF in plain template writing correctly")
	}

	err = i.InterpWriter(WriterAlwaysEOF{}, []byte("%RAW;"))
	if err != io.EOF {
		t.Fatal("Didn't handle error on missing params correctly")
	}

	err = i.InterpWriter(WriterAlwaysEOF{}, []byte("%%;"))
	if err != io.EOF {
		t.Fatal("Didn't handle error on %%; correctly")
	}
}

func TestParameters(t *testing.T) {
	i := NewInterpolator()

	i.AddFormat("p", func(w io.Writer, arg interface{}, params []byte) error {
		w.Write(params)
		w.Write([]byte(arg.(string)))
		return nil
	})

	res, err := i.InterpStr("%p:hi!;", "moo")
	if err != nil || res != "hi!moo" {
		t.Fatal("parameters not handled correctly")
	}
}

func TestCover(t *testing.T) {
	// assert these don't crash

	ErrAlreadyExists("x").Error()
	ErrUnknownFormatter("x").Error()
}

type readBytesTest struct {
	input string
	delim string
	expected string
	error error
}

func TestReadBytesUntilUnesc(t *testing.T) {
	tests := []readBytesTest{
		{"abc", "c", "ab", nil},
		{"abc", "d", "abc", io.EOF},
		{`a\bc`, "c", "ab", nil},
		{`a\\bc`, "c", `a\b`, nil},
		{`abc\`, "d", `abc`, nil},
	}

	for _, test := range tests {
		res, err := readBytesUntilUnescDelim(
			bytes.NewBuffer([]byte(test.input)),
			[]byte(test.delim)[0],
		)
		if test.error != nil && !reflect.DeepEqual(test.error, err) {
			t.Fatal("Failed: wrong error on " + test.input)
		}
		if res != nil && string(res) != test.expected {
			t.Fatal("Failed: wrong result on " + test.input)
		}
	}
}

type WriterAlwaysEOF struct{}

func (wae WriterAlwaysEOF) Write(b []byte) (n int, err error) {
	if len(b) > 0 {
		return 0, io.EOF
	}
	return 0, nil
}
<commit_msg>Create a benchmark to answer questions about the fastest way to call through an interface.<commit_after>package strinterp

import (
	"bytes"
	"errors"
	"fmt"
	"html"
	"io"
	"reflect"
	"testing"
)

type StrescTest struct {
	Format string
	Args   []interface{}
	Result string
	Error  error
}

func TestDefaultInterpolator(t *testing.T) {
	noargs := []interface{}{}

	tests := []StrescTest{
		{"x", noargs, "x", nil},
		{"x%%;y", []interface{}{}, "x%y", nil},
		{"x%RAW;", []interface{}{"y"}, "xy", nil},
		{"x%RAW;", []interface{}{[]byte("y")}, "xy", nil},
		{"x%RAW;", []interface{}{bytes.NewBuffer([]byte("y"))}, "xy", nil},

		{"x%RAW", []interface{}{"y"}, "", ErrIncompleteFormatString},
		{"x%RAW;", []interface{}{0}, "", ErrRawUnknownType},
		{"x%RAW;", []interface{}{}, "x%RAW error: No arg;", nil},
		{"x%blargh;", []interface{}{}, "", ErrUnknownFormatter("blargh")},
	}

	i := NewInterpolator()

	for _, test := range tests {
		res, err := i.InterpStr(test.Format, test.Args...)

		if test.Error != nil && !reflect.DeepEqual(test.Error, err) {
			// note in this case we aren't being hypocritcal... having just
			// established this package's interpolation is actually broken,
			// don't try to use it to output an error message!
			t.Fatal(fmt.Sprintf("for %s, expected error %v, got %v", test.Format, test.Error, err))
		}
		if test.Result != "" && test.Result != res {
			t.Fatal(fmt.Sprintf("for %s, expected result '%s', got '%s'", test.Format, test.Result, res))
		}
	}
}

func TestOtherInterpolators(t *testing.T) {
	i := NewInterpolator()

	err := i.AddFormat("cdata", func(w io.Writer, arg interface{}, params []byte) error {
		switch a := arg.(type) {
		case []byte:
			newBytes := []byte(html.EscapeString(string(a)))
			_, err := w.Write(newBytes)
			return err
		case string:
			newBytes := []byte(html.EscapeString(a))
			_, err := w.Write(newBytes)
			return err
		default:
			return errors.New("unknown type for cdata")
		}
	})

	if err != nil {
		t.Fatal("Can't add new format?")
	}

	err = i.AddFormat("cdata", func(io.Writer, interface{}, []byte) error { return nil })
	if !reflect.DeepEqual(err, ErrAlreadyExists("cdata")) {
		t.Fatal("fails to catch double-type add")
	}

	res, err := i.InterpStr("hello %cdata;", "& stuff")
	if err != nil {
		t.Fatal("Error: " + err.Error())
	}

	if res != "hello &amp; stuff" {
		t.Fatal("Didn't get correct cdata string: " + res)
	}
}

func TestWriterErrors(t *testing.T) {
	i := NewInterpolator()

	err := i.InterpWriter(WriterAlwaysEOF{}, []byte("x"))
	if err != io.EOF {
		t.Fatal("Don't handle EOF in writer correctly")
	}

	err = i.InterpWriter(WriterAlwaysEOF{}, []byte("x%RAW;"), "x")
	if err != io.EOF {
		t.Fatal("Doesn't handle EOF in plain template writing correctly")
	}

	err = i.InterpWriter(WriterAlwaysEOF{}, []byte("%RAW;"))
	if err != io.EOF {
		t.Fatal("Didn't handle error on missing params correctly")
	}

	err = i.InterpWriter(WriterAlwaysEOF{}, []byte("%%;"))
	if err != io.EOF {
		t.Fatal("Didn't handle error on %%; correctly")
	}
}

func TestParameters(t *testing.T) {
	i := NewInterpolator()

	i.AddFormat("p", func(w io.Writer, arg interface{}, params []byte) error {
		w.Write(params)
		w.Write([]byte(arg.(string)))
		return nil
	})

	res, err := i.InterpStr("%p:hi!;", "moo")
	if err != nil || res != "hi!moo" {
		t.Fatal("parameters not handled correctly")
	}
}

func TestCover(t *testing.T) {
	// assert these don't crash

	ErrAlreadyExists("x").Error()
	ErrUnknownFormatter("x").Error()
}

type readBytesTest struct {
	input    string
	delim    string
	expected string
	error    error
}

func TestReadBytesUntilUnesc(t *testing.T) {
	tests := []readBytesTest{
		{"abc", "c", "ab", nil},
		{"abc", "d", "abc", io.EOF},
		{`a\bc`, "c", "ab", nil},
		{`a\\bc`, "c", `a\b`, nil},
		{`abc\`, "d", `abc`, nil},
	}

	for _, test := range tests {
		res, err := readBytesUntilUnescDelim(
			bytes.NewBuffer([]byte(test.input)),
			[]byte(test.delim)[0],
		)
		if test.error != nil && !reflect.DeepEqual(test.error, err) {
			t.Fatal("Failed: wrong error on " + test.input)
		}
		if res != nil && string(res) != test.expected {
			t.Fatal("Failed: wrong result on " + test.input)
		}
	}
}

type WriterAlwaysEOF struct{}

func (wae WriterAlwaysEOF) Write(b []byte) (n int, err error) {
	if len(b) > 0 {
		return 0, io.EOF
	}
	return 0, nil
}

// The purpose of this code is to test whether it is faster to try to
// "memoize" the resolution of the interface method, or if that doesn't
// save any time.
//
// Surprisingly to me, as of Go 1.4, calling straight through the interface
// doesn't just *tie* the "memoized" version, it is *faster* than it. I
// could easily see this changing in future versions, though.
type Blank interface {
	Nothing()
}

type B struct{}

func (b B) Nothing() {}

func BenchmarkCallThroughInterface(b *testing.B) {
	var instance Blank
	instance = B{}

	for i := 0; i < b.N; i++ {
		instance.Nothing()
	}
}

func BenchmarkMemoizedInterfaceCall(b *testing.B) {
	var instance Blank
	instance = B{}

	nothing := instance.Nothing

	for i := 0; i < b.N; i++ {
		nothing()
	}
}

{'fraction_non_alphanumeric': 0.1974726922253159, 'fraction_numerical': 0.0014992503748125937, 'mean_word_length': 3.9282321899736146, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, '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': '7203135', 'n_tokens_mistral': 3679, 'n_tokens_neox': 3374, 'n_words': 907}