<commit_before>package statsd

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"testing"
	"time"
)

const (
	testAddr = ":8126"
	testKey  = "test_key"
)

var testDate = time.Date(2015, 10, 22, 16, 53, 0, 0, time.UTC)

func TestCount(t *testing.T) {
	testOutput(t, "test_key:5|c", func(c *Client) {
		c.Count(testKey, 5, 1)
	})
}

func TestIncrement(t *testing.T) {
	testOutput(t, "test_key:1|c", func(c *Client) {
		c.Increment(testKey)
	})
}

func TestGauge(t *testing.T) {
	testOutput(t, "test_key:5|g\ntest_key:0|g\ntest_key:-10|g", func(c *Client) {
		c.Gauge(testKey, 5)
		c.Gauge(testKey, -10)
	})
}

func TestChangeGauge(t *testing.T) {
	testOutput(t, "test_key:+17|g\ntest_key:-1|g", func(c *Client) {
		c.ChangeGauge(testKey, 17)
		c.ChangeGauge(testKey, -1)
		c.ChangeGauge(testKey, 0)
	})
}

func TestTiming(t *testing.T) {
	testOutput(t, "test_key:6|ms", func(c *Client) {
		c.Timing(testKey, 6, 1)
	})
}

func TestNewTiming(t *testing.T) {
	i := 0
	now = func() time.Time {
		i++
		switch i {
		default:
			return testDate
		case 2:
			return testDate.Add(10 * time.Millisecond)
		case 3:
			return testDate.Add(100 * time.Millisecond)
		case 4:
			return testDate.Add(time.Second)
		}
	}
	defer func() { now = time.Now }()

	testOutput(t, "test_key:10|ms\ntest_key:1000|ms", func(c *Client) {
		timing := c.NewTiming()
		timing.Send(testKey, 1)

		got := timing.Duration().Nanoseconds()
		want := int64(100 * time.Millisecond)
		if got != want {
			t.Errorf("Duration() = %v, want %v", got, want)
		}

		timing.Send(testKey, 1)
	})
}

func TestUnique(t *testing.T) {
	testOutput(t, "test_key:foo|s", func(c *Client) {
		c.Unique(testKey, "foo")
	})
}

func TestSamplingRate(t *testing.T) {
	testOutput(t, "test_key:3|c@0.6\ntest_key:4|ms@0.6", func(c *Client) {
		randFloat = func() float32 { return 0.5 }
		c.Count(testKey, 1, 0.2)
		c.Timing(testKey, 2, 0.3)
		c.Count(testKey, 3, 0.6)
		c.Timing(testKey, 4, 0.6)
	})
}

func TestMute(t *testing.T) {
	dialTimeout = func(string, string, time.Duration) (net.Conn, error) {
		t.Fatal("net.Dial should not be called")
		return nil, nil
	}
	defer func() { dialTimeout = net.DialTimeout }()

	c, err := New("", Mute(true))
	if err != nil {
		t.Errorf("New() = %v", err)
	}
	c.Increment(testKey)
	c.Gauge(testKey, 1)
	c.ChangeGauge(testKey, 1)
	c.Timing(testKey, 1, 1)
	c.Unique(testKey, "1")
	c.Flush()
	c.Close()
}

func TestPrefix(t *testing.T) {
	testOutput(t, "foo.test_key:1|c", func(c *Client) {
		c.Increment(testKey)
	}, WithPrefix("foo."))
}

func TestDatadogTags(t *testing.T) {
	testOutput(t, "test_key:1|c|#tag1:value1,tag2,tag3:value3", func(c *Client) {
		c.Increment(testKey)
	}, WithDatadogTags("tag1:value1", "tag2", "tag3:value3"))
}

func TestInfluxDBTags(t *testing.T) {
	testOutput(t, "test_key,key1=value1,key2=value2:1|c", func(c *Client) {
		c.Increment(testKey)
	}, WithInfluxDBTags("key1", "value1", "key2", "value2"))
}

func TestInfluxDBTagsPanic(t *testing.T) {
	defer func() {
		r := recover()
		if r == nil {
			t.Fatal("WithInfluxDBTags should panic when only one argument is provided")
		}
	}()
	New("", WithInfluxDBTags("key1"))
}

func TestErrorHandler(t *testing.T) {
	errorCount := 0
	testClient(t, func(c *Client) {
		getMockConn(c).err = errors.New("test error")

		c.Increment(testKey)
		c.Close()
		if errorCount != 2 {
			t.Errorf("Wrong error count, got %d, want 2", errorCount)
		}
	}, WithErrorHandler(func(err error) {
		if err == nil {
			t.Error("Error should not be nil")
		}
		errorCount++
	}))
}

func TestFlush(t *testing.T) {
	testClient(t, func(c *Client) {
		c.Increment(testKey)
		c.Flush()
		got := getMockConn(c).buf.String()
		want := "test_key:1|c"
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
		c.Close()
	})
}

func TestFlushPeriod(t *testing.T) {
	testClient(t, func(c *Client) {
		c.Increment(testKey)
		time.Sleep(time.Millisecond)
		c.mu.Lock()
		got := getMockConn(c).buf.String()
		want := "test_key:1|c"
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
		c.mu.Unlock()
		c.Close()
	}, WithFlushPeriod(time.Nanosecond))
}

func TestMaxPacketSize(t *testing.T) {
	testClient(t, func(c *Client) {
		c.Increment(testKey)
		conn := getMockConn(c)
		got := conn.buf.String()
		if got != "" {
			t.Errorf("Output should be empty, got %q", got)
		}

		c.Increment(testKey)
		got = conn.buf.String()
		want := "test_key:1|c"
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
		conn.buf.Reset()
		c.Close()

		got = conn.buf.String()
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
	}, WithMaxPacketSize(15))
}

func TestDialError(t *testing.T) {
	dialTimeout = func(string, string, time.Duration) (net.Conn, error) {
		return nil, errors.New("")
	}
	defer func() { dialTimeout = net.DialTimeout }()

	_, err := New(testAddr)
	if err == nil {
		t.Error("New() did not return an error")
	}
}

func TestConcurrency(t *testing.T) {
	testOutput(t, "test_key:1|c\ntest_key:1|c\ntest_key:1|c", func(c *Client) {
		var wg sync.WaitGroup
		wg.Add(1)
		c.Increment(testKey)
		go func() {
			c.Increment(testKey)
			wg.Done()
		}()
		c.Increment(testKey)
		wg.Wait()
	})
}

func TestUDPNotListening(t *testing.T) {
	dialTimeout = mockUDPClosed
	defer func() { dialTimeout = net.DialTimeout }()

	_, err := New(testAddr)
	if err == nil {
		t.Error("New should return an error")
	}
}

type mockClosedUDPConn struct {
	i int
	net.Conn
}

func (c *mockClosedUDPConn) Write(p []byte) (int, error) {
	c.i++
	if c.i == 2 {
		return 0, errors.New("test error")
	}
	return 0, nil
}

func (c *mockClosedUDPConn) Close() error {
	return nil
}

func mockUDPClosed(string, string, time.Duration) (net.Conn, error) {
	return &mockClosedUDPConn{}, nil
}

func testClient(t *testing.T, f func(*Client), options ...Option) {
	dialTimeout = mockDial
	defer func() { dialTimeout = net.DialTimeout }()

	options = append([]Option{
		WithFlushPeriod(0),
		WithErrorHandler(expectNoError(t)),
	}, options...)
	c, err := New(testAddr, options...)
	if err != nil {
		t.Fatalf("New: %v", err)
	}

	f(c)
}

func testOutput(t *testing.T, want string, f func(*Client), options ...Option) {
	testClient(t, func(c *Client) {
		f(c)
		c.Close()

		got := c.conn.(*mockConn).buf.String()
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
	}, options...)
}

func expectNoError(t *testing.T) func(error) {
	return func(err error) {
		t.Errorf("ErrorHandler should not receive an error: %v", err)
	}
}

type mockConn struct {
	buf bytes.Buffer
	err error
	net.Conn
}

func (c *mockConn) Write(p []byte) (int, error) {
	if c.err != nil {
		return 0, c.err
	}
	return c.buf.Write(p)
}

func (c *mockConn) Close() error {
	return c.err
}

func getMockConn(c *Client) *mockConn {
	if mock, ok := c.conn.(*mockConn); ok {
		return mock
	}
	return nil
}

func mockDial(string, string, time.Duration) (net.Conn, error) {
	return &mockConn{}, nil
}

func TestUDP(t *testing.T) {
	testNetwork(t, "udp")
}

func TestTCP(t *testing.T) {
	testNetwork(t, "tcp")
}

func testNetwork(t *testing.T, network string) {
	received := make(chan bool)
	server := newServer(t, network, testAddr, func(p []byte) {
		s := string(p)
		if s != "test_key:1|c" {
			t.Errorf("invalid output: %q", s)
		}
		received <- true
	})
	defer server.Close()

	c, err := New(testAddr,
		WithNetwork(network), WithErrorHandler(expectNoError(t)))
	if err != nil {
		t.Fatalf("New: %v", err)
	}

	c.Increment(testKey)
	c.Close()
	select {
	case <-time.After(100 * time.Millisecond):
		t.Error("server received nothing after 100ms")
	case <-received:
	}
}

type server struct {
	t      testing.TB
	closer io.Closer
	closed chan bool
}

func newServer(t testing.TB, network, addr string, f func([]byte)) *server {
	s := &server{t: t, closed: make(chan bool)}
	switch network {
	case "udp":
		laddr, err := net.ResolveUDPAddr("udp", addr)
		if err != nil {
			t.Fatal(err)
		}
		conn, err := net.ListenUDP("udp", laddr)
		if err != nil {
			t.Fatal(err)
		}
		s.closer = conn
		go func() {
			buf := make([]byte, 1024)
			for {
				n, err := conn.Read(buf)
				if err != nil {
					s.closed <- true
					return
				}
				if n > 0 {
					f(buf[:n])
				}
			}
		}()
	case "tcp":
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			t.Fatal(err)
		}
		s.closer = ln
		go func() {
			for {
				conn, err := ln.Accept()
				if err != nil {
					s.closed <- true
					return
				}
				p, err := ioutil.ReadAll(conn)
				if err != nil {
					t.Fatal(err)
				}
				if err := conn.Close(); err != nil {
					t.Fatal(err)
				}
				f(p)
			}
		}()
	default:
		t.Fatalf("Invalid network: %q", network)
	}

	return s
}

func (s *server) Close() {
	if err := s.closer.Close(); err != nil {
		s.t.Error(err)
	}
	<-s.closed
}

func Benchmark(b *testing.B) {
	s := newServer(b, "udp", testAddr, func([]byte) {})
	c, err := New(testAddr, WithFlushPeriod(0))
	if err != nil {
		b.Fatal(err)
	}
	for i := 0; i < b.N; i++ {
		c.Increment(testKey)
		c.Count(testKey, 17, .5)
		c.Gauge(testKey, 17)
		c.Timing(testKey, 17, 1)
		c.NewTiming().Send(testKey, 1)
	}
	c.Close()
	s.Close()
}
<commit_msg>Used a random available port in tests<commit_after>package statsd

import (
	"bytes"
	"errors"
	"io"
	"io/ioutil"
	"net"
	"sync"
	"testing"
	"time"
)

const (
	testAddr = ":0"
	testKey  = "test_key"
)

var testDate = time.Date(2015, 10, 22, 16, 53, 0, 0, time.UTC)

func TestCount(t *testing.T) {
	testOutput(t, "test_key:5|c", func(c *Client) {
		c.Count(testKey, 5, 1)
	})
}

func TestIncrement(t *testing.T) {
	testOutput(t, "test_key:1|c", func(c *Client) {
		c.Increment(testKey)
	})
}

func TestGauge(t *testing.T) {
	testOutput(t, "test_key:5|g\ntest_key:0|g\ntest_key:-10|g", func(c *Client) {
		c.Gauge(testKey, 5)
		c.Gauge(testKey, -10)
	})
}

func TestChangeGauge(t *testing.T) {
	testOutput(t, "test_key:+17|g\ntest_key:-1|g", func(c *Client) {
		c.ChangeGauge(testKey, 17)
		c.ChangeGauge(testKey, -1)
		c.ChangeGauge(testKey, 0)
	})
}

func TestTiming(t *testing.T) {
	testOutput(t, "test_key:6|ms", func(c *Client) {
		c.Timing(testKey, 6, 1)
	})
}

func TestNewTiming(t *testing.T) {
	i := 0
	now = func() time.Time {
		i++
		switch i {
		default:
			return testDate
		case 2:
			return testDate.Add(10 * time.Millisecond)
		case 3:
			return testDate.Add(100 * time.Millisecond)
		case 4:
			return testDate.Add(time.Second)
		}
	}
	defer func() { now = time.Now }()

	testOutput(t, "test_key:10|ms\ntest_key:1000|ms", func(c *Client) {
		timing := c.NewTiming()
		timing.Send(testKey, 1)

		got := timing.Duration().Nanoseconds()
		want := int64(100 * time.Millisecond)
		if got != want {
			t.Errorf("Duration() = %v, want %v", got, want)
		}

		timing.Send(testKey, 1)
	})
}

func TestUnique(t *testing.T) {
	testOutput(t, "test_key:foo|s", func(c *Client) {
		c.Unique(testKey, "foo")
	})
}

func TestSamplingRate(t *testing.T) {
	testOutput(t, "test_key:3|c@0.6\ntest_key:4|ms@0.6", func(c *Client) {
		randFloat = func() float32 { return 0.5 }
		c.Count(testKey, 1, 0.2)
		c.Timing(testKey, 2, 0.3)
		c.Count(testKey, 3, 0.6)
		c.Timing(testKey, 4, 0.6)
	})
}

func TestMute(t *testing.T) {
	dialTimeout = func(string, string, time.Duration) (net.Conn, error) {
		t.Fatal("net.Dial should not be called")
		return nil, nil
	}
	defer func() { dialTimeout = net.DialTimeout }()

	c, err := New("", Mute(true))
	if err != nil {
		t.Errorf("New() = %v", err)
	}
	c.Increment(testKey)
	c.Gauge(testKey, 1)
	c.ChangeGauge(testKey, 1)
	c.Timing(testKey, 1, 1)
	c.Unique(testKey, "1")
	c.Flush()
	c.Close()
}

func TestPrefix(t *testing.T) {
	testOutput(t, "foo.test_key:1|c", func(c *Client) {
		c.Increment(testKey)
	}, WithPrefix("foo."))
}

func TestDatadogTags(t *testing.T) {
	testOutput(t, "test_key:1|c|#tag1:value1,tag2,tag3:value3", func(c *Client) {
		c.Increment(testKey)
	}, WithDatadogTags("tag1:value1", "tag2", "tag3:value3"))
}

func TestInfluxDBTags(t *testing.T) {
	testOutput(t, "test_key,key1=value1,key2=value2:1|c", func(c *Client) {
		c.Increment(testKey)
	}, WithInfluxDBTags("key1", "value1", "key2", "value2"))
}

func TestInfluxDBTagsPanic(t *testing.T) {
	defer func() {
		r := recover()
		if r == nil {
			t.Fatal("WithInfluxDBTags should panic when only one argument is provided")
		}
	}()
	New("", WithInfluxDBTags("key1"))
}

func TestErrorHandler(t *testing.T) {
	errorCount := 0
	testClient(t, func(c *Client) {
		getMockConn(c).err = errors.New("test error")

		c.Increment(testKey)
		c.Close()
		if errorCount != 2 {
			t.Errorf("Wrong error count, got %d, want 2", errorCount)
		}
	}, WithErrorHandler(func(err error) {
		if err == nil {
			t.Error("Error should not be nil")
		}
		errorCount++
	}))
}

func TestFlush(t *testing.T) {
	testClient(t, func(c *Client) {
		c.Increment(testKey)
		c.Flush()
		got := getMockConn(c).buf.String()
		want := "test_key:1|c"
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
		c.Close()
	})
}

func TestFlushPeriod(t *testing.T) {
	testClient(t, func(c *Client) {
		c.Increment(testKey)
		time.Sleep(time.Millisecond)
		c.mu.Lock()
		got := getMockConn(c).buf.String()
		want := "test_key:1|c"
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
		c.mu.Unlock()
		c.Close()
	}, WithFlushPeriod(time.Nanosecond))
}

func TestMaxPacketSize(t *testing.T) {
	testClient(t, func(c *Client) {
		c.Increment(testKey)
		conn := getMockConn(c)
		got := conn.buf.String()
		if got != "" {
			t.Errorf("Output should be empty, got %q", got)
		}

		c.Increment(testKey)
		got = conn.buf.String()
		want := "test_key:1|c"
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
		conn.buf.Reset()
		c.Close()

		got = conn.buf.String()
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
	}, WithMaxPacketSize(15))
}

func TestDialError(t *testing.T) {
	dialTimeout = func(string, string, time.Duration) (net.Conn, error) {
		return nil, errors.New("")
	}
	defer func() { dialTimeout = net.DialTimeout }()

	_, err := New(testAddr)
	if err == nil {
		t.Error("New() did not return an error")
	}
}

func TestConcurrency(t *testing.T) {
	testOutput(t, "test_key:1|c\ntest_key:1|c\ntest_key:1|c", func(c *Client) {
		var wg sync.WaitGroup
		wg.Add(1)
		c.Increment(testKey)
		go func() {
			c.Increment(testKey)
			wg.Done()
		}()
		c.Increment(testKey)
		wg.Wait()
	})
}

func TestUDPNotListening(t *testing.T) {
	dialTimeout = mockUDPClosed
	defer func() { dialTimeout = net.DialTimeout }()

	_, err := New(testAddr)
	if err == nil {
		t.Error("New should return an error")
	}
}

type mockClosedUDPConn struct {
	i int
	net.Conn
}

func (c *mockClosedUDPConn) Write(p []byte) (int, error) {
	c.i++
	if c.i == 2 {
		return 0, errors.New("test error")
	}
	return 0, nil
}

func (c *mockClosedUDPConn) Close() error {
	return nil
}

func mockUDPClosed(string, string, time.Duration) (net.Conn, error) {
	return &mockClosedUDPConn{}, nil
}

func testClient(t *testing.T, f func(*Client), options ...Option) {
	dialTimeout = mockDial
	defer func() { dialTimeout = net.DialTimeout }()

	options = append([]Option{
		WithFlushPeriod(0),
		WithErrorHandler(expectNoError(t)),
	}, options...)
	c, err := New(testAddr, options...)
	if err != nil {
		t.Fatalf("New: %v", err)
	}

	f(c)
}

func testOutput(t *testing.T, want string, f func(*Client), options ...Option) {
	testClient(t, func(c *Client) {
		f(c)
		c.Close()

		got := c.conn.(*mockConn).buf.String()
		if got != want {
			t.Errorf("Invalid output, got %q, want %q", got, want)
		}
	}, options...)
}

func expectNoError(t *testing.T) func(error) {
	return func(err error) {
		t.Errorf("ErrorHandler should not receive an error: %v", err)
	}
}

type mockConn struct {
	buf bytes.Buffer
	err error
	net.Conn
}

func (c *mockConn) Write(p []byte) (int, error) {
	if c.err != nil {
		return 0, c.err
	}
	return c.buf.Write(p)
}

func (c *mockConn) Close() error {
	return c.err
}

func getMockConn(c *Client) *mockConn {
	if mock, ok := c.conn.(*mockConn); ok {
		return mock
	}
	return nil
}

func mockDial(string, string, time.Duration) (net.Conn, error) {
	return &mockConn{}, nil
}

func TestUDP(t *testing.T) {
	testNetwork(t, "udp")
}

func TestTCP(t *testing.T) {
	testNetwork(t, "tcp")
}

func testNetwork(t *testing.T, network string) {
	received := make(chan bool)
	server := newServer(t, network, testAddr, func(p []byte) {
		s := string(p)
		if s != "test_key:1|c" {
			t.Errorf("invalid output: %q", s)
		}
		received <- true
	})
	defer server.Close()

	c, err := New(server.addr,
		WithNetwork(network), WithErrorHandler(expectNoError(t)))
	if err != nil {
		t.Fatalf("New: %v", err)
	}

	c.Increment(testKey)
	c.Close()
	select {
	case <-time.After(100 * time.Millisecond):
		t.Error("server received nothing after 100ms")
	case <-received:
	}
}

type server struct {
	t      testing.TB
	addr   string
	closer io.Closer
	closed chan bool
}

func newServer(t testing.TB, network, addr string, f func([]byte)) *server {
	s := &server{t: t, closed: make(chan bool)}
	switch network {
	case "udp":
		laddr, err := net.ResolveUDPAddr("udp", addr)
		if err != nil {
			t.Fatal(err)
		}
		conn, err := net.ListenUDP("udp", laddr)
		if err != nil {
			t.Fatal(err)
		}
		s.closer = conn
		s.addr = conn.LocalAddr().String()
		go func() {
			buf := make([]byte, 1024)
			for {
				n, err := conn.Read(buf)
				if err != nil {
					s.closed <- true
					return
				}
				if n > 0 {
					f(buf[:n])
				}
			}
		}()
	case "tcp":
		ln, err := net.Listen("tcp", addr)
		if err != nil {
			t.Fatal(err)
		}
		s.closer = ln
		s.addr = ln.Addr().String()
		go func() {
			for {
				conn, err := ln.Accept()
				if err != nil {
					s.closed <- true
					return
				}
				p, err := ioutil.ReadAll(conn)
				if err != nil {
					t.Fatal(err)
				}
				if err := conn.Close(); err != nil {
					t.Fatal(err)
				}
				f(p)
			}
		}()
	default:
		t.Fatalf("Invalid network: %q", network)
	}

	return s
}

func (s *server) Close() {
	if err := s.closer.Close(); err != nil {
		s.t.Error(err)
	}
	<-s.closed
}

func Benchmark(b *testing.B) {
	s := newServer(b, "udp", testAddr, func([]byte) {})
	c, err := New(s.addr, WithFlushPeriod(0))
	if err != nil {
		b.Fatal(err)
	}
	for i := 0; i < b.N; i++ {
		c.Increment(testKey)
		c.Count(testKey, 17, .5)
		c.Gauge(testKey, 17)
		c.Timing(testKey, 17, 1)
		c.NewTiming().Send(testKey, 1)
	}
	c.Close()
	s.Close()
}
<|endoftext|>


<commit_before>package merkle

import (
	"crypto/sha512"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"
	"testing"
)

func TestMerkleHashWriterLargeChunk(t *testing.T) {
	// make a large enough test file of increments, corresponding to our blockSize
	bs := 512 * 1024
	fh, err := ioutil.TempFile("", "merkleChunks.")
	if err != nil {
		t.Fatal(err)
	}
	defer fh.Close()
	defer os.Remove(fh.Name())

	for i := 0; i < 5; i++ {
		b := []byte{byte(i)}
		var b2 []byte
		for j := 0; j < bs; j++ {
			b2 = append(b2, b...)
		}
		fh.Write(b2)
	}
	if err := fh.Sync(); err != nil {
		t.Fatal(err)
	}
	if _, err := fh.Seek(0, 0); err != nil {
		t.Fatal(err)
	}

	expectedSums := []string{
		"6a521e1d2a632c26e53b83d2cc4b0edecfc1e68c", // 0's
		"316c136d75ffdeb6ac5f1262c45dd8c6ec50fd85", // 1's
		"a56e9c245b9c50d61a91c6c4299813b5e6313722", // 2's
		"58bed752c036310cc48d9dd0d25c4ee9ad0d7ff1", // 3's
		"bf382d8394213b897424803c27f3e2ec2223e5fd", // 4's
	}

	h := NewHash(DefaultHashMaker, bs)
	if _, err = io.Copy(h, fh); err != nil {
		t.Fatal(err)
	}
	h.Sum(nil)
	for i, node := range h.Nodes() {
		c, err := node.Checksum()
		if err != nil {
			t.Fatal(err)
		}
		if cs := fmt.Sprintf("%x", c); cs != expectedSums[i] {
			t.Errorf("expected sum %q; got %q", expectedSums[i], cs)
		}
	}
}

func TestMerkleHashWriter(t *testing.T) {
	msg := []byte("the quick brown fox jumps over the lazy dog")
	expectedSum := "48940c1c72636648ad40aa59c162f2208e835b38"

	h := NewHash(DefaultHashMaker, 10)
	i, err := h.Write(msg)
	if err != nil {
		t.Fatal(err)
	}
	if i != len(msg) {
		t.Fatalf("expected to write %d, only wrote %d", len(msg), i)
	}

	// We're left with a partial lastBlock
	expectedNum := 4
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}

	// Next test Sum()
	gotSum := fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum != gotSum {
		t.Errorf("expected initial checksum %q; got %q", expectedSum, gotSum)
	}

	// count blocks again, we should get 5 nodes now
	expectedNum = 5
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}

	// Test Sum() again, ensure same sum
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum != gotSum {
		t.Errorf("expected checksum %q; got %q", expectedSum, gotSum)
	}

	// test that Reset() nulls us out
	h.Reset()
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum == gotSum {
		t.Errorf("expected reset checksum to not equal %q; got %q", expectedSum, gotSum)
	}

	// write our msg again and get the same sum
	i, err = h.Write(msg)
	if err != nil {
		t.Fatal(err)
	}
	if i != len(msg) {
		t.Fatalf("expected to write %d, only wrote %d", len(msg), i)
	}
	// Test Sum(), ensure same sum
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum != gotSum {
		t.Errorf("expected checksum %q; got %q", expectedSum, gotSum)
	}

	// Write more. This should pop the last node, and use the lastBlock.
	i, err = h.Write(msg)
	if err != nil {
		t.Fatal(err)
	}
	if i != len(msg) {
		t.Fatalf("expected to write %d, only wrote %d", len(msg), i)
	}
	expectedNum = 9
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum == gotSum {
		t.Errorf("expected reset checksum to not equal %q; got %q", expectedSum, gotSum)
	}
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}

}

var benchDefault = NewHash(DefaultHashMaker, 8192)
var benchSha512 = NewHash(func() hash.Hash { return sha512.New() }, 8192)
var buf = make([]byte, 8192)

func benchmarkSize(bench hash.Hash, b *testing.B, size int) {
	b.SetBytes(int64(size))
	sum := make([]byte, bench.Size())
	for i := 0; i < b.N; i++ {
		bench.Reset()
		bench.Write(buf[:size])
		bench.Sum(sum[:0])
	}
}

func BenchmarkHash8Bytes(b *testing.B) {
	benchmarkSize(benchDefault, b, 8)
}

func BenchmarkHash1K(b *testing.B) {
	benchmarkSize(benchDefault, b, 1024)
}

func BenchmarkHash8K(b *testing.B) {
	benchmarkSize(benchDefault, b, 8192)
}

func BenchmarkSha512Hash8Bytes(b *testing.B) {
	benchmarkSize(benchSha512, b, 8)
}

func BenchmarkSha512Hash1K(b *testing.B) {
	benchmarkSize(benchSha512, b, 1024)
}

func BenchmarkSha512Hash8K(b *testing.B) {
	benchmarkSize(benchSha512, b, 8192)
}
<commit_msg>benchmark: and compare sha256 as well<commit_after>package merkle

import (
	"crypto/sha256"
	"crypto/sha512"
	"fmt"
	"hash"
	"io"
	"io/ioutil"
	"os"
	"testing"
)

func TestMerkleHashWriterLargeChunk(t *testing.T) {
	// make a large enough test file of increments, corresponding to our blockSize
	bs := 512 * 1024
	fh, err := ioutil.TempFile("", "merkleChunks.")
	if err != nil {
		t.Fatal(err)
	}
	defer fh.Close()
	defer os.Remove(fh.Name())

	for i := 0; i < 5; i++ {
		b := []byte{byte(i)}
		var b2 []byte
		for j := 0; j < bs; j++ {
			b2 = append(b2, b...)
		}
		fh.Write(b2)
	}
	if err := fh.Sync(); err != nil {
		t.Fatal(err)
	}
	if _, err := fh.Seek(0, 0); err != nil {
		t.Fatal(err)
	}

	expectedSums := []string{
		"6a521e1d2a632c26e53b83d2cc4b0edecfc1e68c", // 0's
		"316c136d75ffdeb6ac5f1262c45dd8c6ec50fd85", // 1's
		"a56e9c245b9c50d61a91c6c4299813b5e6313722", // 2's
		"58bed752c036310cc48d9dd0d25c4ee9ad0d7ff1", // 3's
		"bf382d8394213b897424803c27f3e2ec2223e5fd", // 4's
	}

	h := NewHash(DefaultHashMaker, bs)
	if _, err = io.Copy(h, fh); err != nil {
		t.Fatal(err)
	}
	h.Sum(nil)
	for i, node := range h.Nodes() {
		c, err := node.Checksum()
		if err != nil {
			t.Fatal(err)
		}
		if cs := fmt.Sprintf("%x", c); cs != expectedSums[i] {
			t.Errorf("expected sum %q; got %q", expectedSums[i], cs)
		}
	}
}

func TestMerkleHashWriter(t *testing.T) {
	msg := []byte("the quick brown fox jumps over the lazy dog")
	expectedSum := "48940c1c72636648ad40aa59c162f2208e835b38"

	h := NewHash(DefaultHashMaker, 10)
	i, err := h.Write(msg)
	if err != nil {
		t.Fatal(err)
	}
	if i != len(msg) {
		t.Fatalf("expected to write %d, only wrote %d", len(msg), i)
	}

	// We're left with a partial lastBlock
	expectedNum := 4
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}

	// Next test Sum()
	gotSum := fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum != gotSum {
		t.Errorf("expected initial checksum %q; got %q", expectedSum, gotSum)
	}

	// count blocks again, we should get 5 nodes now
	expectedNum = 5
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}

	// Test Sum() again, ensure same sum
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum != gotSum {
		t.Errorf("expected checksum %q; got %q", expectedSum, gotSum)
	}

	// test that Reset() nulls us out
	h.Reset()
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum == gotSum {
		t.Errorf("expected reset checksum to not equal %q; got %q", expectedSum, gotSum)
	}

	// write our msg again and get the same sum
	i, err = h.Write(msg)
	if err != nil {
		t.Fatal(err)
	}
	if i != len(msg) {
		t.Fatalf("expected to write %d, only wrote %d", len(msg), i)
	}
	// Test Sum(), ensure same sum
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum != gotSum {
		t.Errorf("expected checksum %q; got %q", expectedSum, gotSum)
	}

	// Write more. This should pop the last node, and use the lastBlock.
	i, err = h.Write(msg)
	if err != nil {
		t.Fatal(err)
	}
	if i != len(msg) {
		t.Fatalf("expected to write %d, only wrote %d", len(msg), i)
	}
	expectedNum = 9
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}
	gotSum = fmt.Sprintf("%x", h.Sum(nil))
	if expectedSum == gotSum {
		t.Errorf("expected reset checksum to not equal %q; got %q", expectedSum, gotSum)
	}
	if len(h.Nodes()) != expectedNum {
		t.Errorf("expected %d nodes, got %d", expectedNum, len(h.Nodes()))
	}

}

var benchDefault = NewHash(DefaultHashMaker, 8192)
var benchSha256 = NewHash(func() hash.Hash { return sha256.New() }, 8192)
var benchSha512 = NewHash(func() hash.Hash { return sha512.New() }, 8192)
var buf = make([]byte, 8192)

func benchmarkSize(bench hash.Hash, b *testing.B, size int) {
	b.SetBytes(int64(size))
	sum := make([]byte, bench.Size())
	for i := 0; i < b.N; i++ {
		bench.Reset()
		bench.Write(buf[:size])
		bench.Sum(sum[:0])
	}
}

func BenchmarkHash8Bytes(b *testing.B) {
	benchmarkSize(benchDefault, b, 8)
}

func BenchmarkHash1K(b *testing.B) {
	benchmarkSize(benchDefault, b, 1024)
}

func BenchmarkHash8K(b *testing.B) {
	benchmarkSize(benchDefault, b, 8192)
}

func BenchmarkSha256Hash8Bytes(b *testing.B) {
	benchmarkSize(benchSha256, b, 8)
}

func BenchmarkSha256Hash1K(b *testing.B) {
	benchmarkSize(benchSha256, b, 1024)
}

func BenchmarkSha256Hash8K(b *testing.B) {
	benchmarkSize(benchSha256, b, 8192)
}

func BenchmarkSha512Hash8Bytes(b *testing.B) {
	benchmarkSize(benchSha512, b, 8)
}

func BenchmarkSha512Hash1K(b *testing.B) {
	benchmarkSize(benchSha512, b, 1024)
}

func BenchmarkSha512Hash8K(b *testing.B) {
	benchmarkSize(benchSha512, b, 8192)
}
<|endoftext|>


<commit_before>package interpreter

import "io"
import "os"
import "os/exec"
import "regexp"

type NextT int

const (
	THROUGH  NextT = 0
	CONTINUE NextT = 1
	SHUTDOWN NextT = 2
)

type Interpreter struct {
	exec.Cmd
	HookCount    int
	IsBackGround bool
	Closer       io.Closer
}

func New() *Interpreter {
	return new(Interpreter)
}

func (this *Interpreter) Clone() *Interpreter {
	rv := new(Interpreter)
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.Stdin = this.Stdin
	rv.HookCount = this.HookCount
	return this
}

type ArgsHookT func(args []string) []string

var argsHook = func(args []string) []string {
	return args
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(*Interpreter) (NextT, error)

var hook = func(*Interpreter) (NextT, error) {
	return THROUGH, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var errorStatusPattern = regexp.MustCompile("^exit status ([0-9]+)")
var ErrorLevel string

func (this *Interpreter) Interpret(text string) (NextT, error) {
	statements := Parse(text)
	for _, pipeline := range statements {
		var pipeIn *os.File = nil
		for _, state := range pipeline {
			//fmt.Println(state)
			cmd := this.Clone()
			if this.Stderr != nil {
				cmd.Stdin = this.Stdin
			} else {
				cmd.Stdin = os.Stdin
			}
			if this.Stdout != nil {
				cmd.Stdout = this.Stdout
			} else {
				cmd.Stdout = os.Stdout
			}
			if this.Stderr != nil {
				cmd.Stderr = this.Stderr
			} else {
				cmd.Stderr = os.Stderr
			}
			if pipeIn != nil {
				cmd.Stdin = pipeIn
				pipeIn = nil
			}
			if state.Redirect[0].Path != "" {
				fd, err := os.Open(state.Redirect[0].Path)
				if err != nil {
					return CONTINUE, err
				}
				defer fd.Close()
				cmd.Stdin = fd
			}
			if state.Redirect[1].Path != "" {
				var fd *os.File
				var err error
				if state.Redirect[1].IsAppend {
					fd, err = os.OpenFile(state.Redirect[1].Path, os.O_APPEND, 0666)
				} else {
					fd, err = os.OpenFile(state.Redirect[1].Path, os.O_CREATE, 0666)
				}
				if err != nil {
					return CONTINUE, err
				}
				defer fd.Close()
				cmd.Stdout = fd
			}
			if state.Redirect[2].Path != "" {
				var fd *os.File
				var err error
				if state.Redirect[2].IsAppend {
					fd, err = os.OpenFile(state.Redirect[2].Path, os.O_APPEND, 0666)
				} else {
					fd, err = os.OpenFile(state.Redirect[2].Path, os.O_CREATE, 0666)
				}
				if err != nil {
					return CONTINUE, err
				}
				defer fd.Close()
				cmd.Stderr = fd
			}
			var err error = nil
			var pipeOut *os.File = nil
			if state.Term == "|" {
				pipeIn, pipeOut, err = os.Pipe()
				if err != nil {
					return CONTINUE, err
				}
				defer pipeIn.Close()
				cmd.Stdout = pipeOut
			}
			var whatToDo NextT

			isBackGround := (state.Term == "|" || state.Term == "&")

			if len(state.Argv) > 0 {
				if argsHook != nil {
					state.Argv = argsHook(state.Argv)
				}
				cmd.Args = state.Argv
				cmd.IsBackGround = isBackGround
				cmd.Closer = pipeOut
				whatToDo, err = hook(cmd)
				if whatToDo == THROUGH {
					cmd.Path, err = exec.LookPath(state.Argv[0])
					if err == nil {
						if isBackGround {
							err = cmd.Start()
						} else {
							err = cmd.Run()
						}
					}
				} else {
					pipeOut = nil
				}
			}
			if pipeOut != nil {
				pipeOut.Close()
			}
			if err != nil {
				m := errorStatusPattern.FindStringSubmatch(err.Error())
				if m != nil {
					ErrorLevel = m[1]
					err = nil
				} else {
					ErrorLevel = "-1"
				}
			} else {
				ErrorLevel = "0"
			}
			if whatToDo == SHUTDOWN {
				return SHUTDOWN, err
			}
			if err != nil {
				return CONTINUE, err
			}
		}
	}
	return CONTINUE, nil
}

func Run(text string) (NextT, error) {
	return New().Interpret(text)
}
<commit_msg>使用していない interpreter.Run を削除<commit_after>package interpreter

import "io"
import "os"
import "os/exec"
import "regexp"

type NextT int

const (
	THROUGH  NextT = 0
	CONTINUE NextT = 1
	SHUTDOWN NextT = 2
)

type Interpreter struct {
	exec.Cmd
	HookCount    int
	IsBackGround bool
	Closer       io.Closer
}

func New() *Interpreter {
	return new(Interpreter)
}

func (this *Interpreter) Clone() *Interpreter {
	rv := new(Interpreter)
	rv.Stdout = this.Stdout
	rv.Stderr = this.Stderr
	rv.Stdin = this.Stdin
	rv.HookCount = this.HookCount
	return this
}

type ArgsHookT func(args []string) []string

var argsHook = func(args []string) []string {
	return args
}

func SetArgsHook(argsHook_ ArgsHookT) (rv ArgsHookT) {
	rv, argsHook = argsHook, argsHook_
	return
}

type HookT func(*Interpreter) (NextT, error)

var hook = func(*Interpreter) (NextT, error) {
	return THROUGH, nil
}

func SetHook(hook_ HookT) (rv HookT) {
	rv, hook = hook, hook_
	return
}

var errorStatusPattern = regexp.MustCompile("^exit status ([0-9]+)")
var ErrorLevel string

func (this *Interpreter) Interpret(text string) (NextT, error) {
	statements := Parse(text)
	for _, pipeline := range statements {
		var pipeIn *os.File = nil
		for _, state := range pipeline {
			//fmt.Println(state)
			cmd := this.Clone()
			if this.Stderr != nil {
				cmd.Stdin = this.Stdin
			} else {
				cmd.Stdin = os.Stdin
			}
			if this.Stdout != nil {
				cmd.Stdout = this.Stdout
			} else {
				cmd.Stdout = os.Stdout
			}
			if this.Stderr != nil {
				cmd.Stderr = this.Stderr
			} else {
				cmd.Stderr = os.Stderr
			}
			if pipeIn != nil {
				cmd.Stdin = pipeIn
				pipeIn = nil
			}
			if state.Redirect[0].Path != "" {
				fd, err := os.Open(state.Redirect[0].Path)
				if err != nil {
					return CONTINUE, err
				}
				defer fd.Close()
				cmd.Stdin = fd
			}
			if state.Redirect[1].Path != "" {
				var fd *os.File
				var err error
				if state.Redirect[1].IsAppend {
					fd, err = os.OpenFile(state.Redirect[1].Path, os.O_APPEND, 0666)
				} else {
					fd, err = os.OpenFile(state.Redirect[1].Path, os.O_CREATE, 0666)
				}
				if err != nil {
					return CONTINUE, err
				}
				defer fd.Close()
				cmd.Stdout = fd
			}
			if state.Redirect[2].Path != "" {
				var fd *os.File
				var err error
				if state.Redirect[2].IsAppend {
					fd, err = os.OpenFile(state.Redirect[2].Path, os.O_APPEND, 0666)
				} else {
					fd, err = os.OpenFile(state.Redirect[2].Path, os.O_CREATE, 0666)
				}
				if err != nil {
					return CONTINUE, err
				}
				defer fd.Close()
				cmd.Stderr = fd
			}
			var err error = nil
			var pipeOut *os.File = nil
			if state.Term == "|" {
				pipeIn, pipeOut, err = os.Pipe()
				if err != nil {
					return CONTINUE, err
				}
				defer pipeIn.Close()
				cmd.Stdout = pipeOut
			}
			var whatToDo NextT

			isBackGround := (state.Term == "|" || state.Term == "&")

			if len(state.Argv) > 0 {
				if argsHook != nil {
					state.Argv = argsHook(state.Argv)
				}
				cmd.Args = state.Argv
				cmd.IsBackGround = isBackGround
				cmd.Closer = pipeOut
				whatToDo, err = hook(cmd)
				if whatToDo == THROUGH {
					cmd.Path, err = exec.LookPath(state.Argv[0])
					if err == nil {
						if isBackGround {
							err = cmd.Start()
						} else {
							err = cmd.Run()
						}
					}
				} else {
					pipeOut = nil
				}
			}
			if pipeOut != nil {
				pipeOut.Close()
			}
			if err != nil {
				m := errorStatusPattern.FindStringSubmatch(err.Error())
				if m != nil {
					ErrorLevel = m[1]
					err = nil
				} else {
					ErrorLevel = "-1"
				}
			} else {
				ErrorLevel = "0"
			}
			if whatToDo == SHUTDOWN {
				return SHUTDOWN, err
			}
			if err != nil {
				return CONTINUE, err
			}
		}
	}
	return CONTINUE, nil
}
<|endoftext|>


<commit_before>package suit

import (
	"encoding/json"
	"fmt"
	"reflect"
	"unicode"
	"unicode/utf8"
)

type ConfigurationScreen struct {
	Title       string
	Subtitle    string
	DisplayIcon string
	Sections    []Section
	Actions     []Typed
}

func (o *ConfigurationScreen) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

type Section struct {
	Title    string
	Subtitle string
	Contents []Typed
	Well     bool
}

type InputText struct {
	Title       string
	Subtitle    string
	Before      string
	After       string
	Placeholder string
	Name        string
	Value       interface{}
	InputType   string
	Minimum     *int
	Maximum     *int
}

func (o InputText) getType() string {
	return "inputText"
}

type StaticText struct {
	Title    string
	Subtitle string
	Before   string
	After    string
	Value    string
}

func (o StaticText) getType() string {
	return "staticText"
}

type InputTime struct {
	Title    string
	Subtitle string
	Before   string
	After    string
	Name     string
	Value    string
}

func (o InputTime) getType() string {
	return "inputTime"
}

type Separator struct {
}

func (o Separator) getType() string {
	return "separator"
}

type OptionGroup struct {
	Title          string
	Subtitle       string
	Name           string
	MinimumChoices int
	MaximumChoices int
	Options        []OptionGroupOption
}

func (o OptionGroup) getType() string {
	return "optionGroup"
}

type OptionGroupOption struct {
	Title    string
	Subtitle string
	Value    string
	Selected bool
}

type RadioGroup struct {
	Title    string
	Subtitle string
	Name     string
	Value    string
	Options  []RadioGroupOption
}

func (o RadioGroup) getType() string {
	return "radioGroup"
}

type RadioGroupOption struct {
	Title       string
	Value       string
	DisplayIcon string
}

type Alert struct {
	Title        string
	Subtitle     string
	DisplayClass string
	DisplayIcon  string
}

func (o Alert) getType() string {
	return "alert"
}

type ActionList struct {
	Title           string
	Subtitle        string
	Name            string
	Options         []ActionListOption
	PrimaryAction   Typed
	SecondaryAction Typed
}

func (o ActionList) getType() string {
	return "actionList"
}

type ActionListOption struct {
	Title    string
	Subtitle string
	Value    string
}

type InputTimeRange struct {
	Title    string
	Subtitle string
	Name     string
	Value    TimeRange
}

func (o InputTimeRange) getType() string {
	return "inputTimeRange"
}

type TimeRange struct {
	From string `json:"from"`
	To   string `json:"to"`
}

type InputHidden struct {
	Name  string
	Value string
}

func (o InputHidden) getType() string {
	return "inputHidden"
}

type CloseAction struct {
	Label string
}

func (o CloseAction) getType() string {
	return "close"
}

type ReplyAction struct {
	Label        string
	Name         string
	DisplayClass string
	DisplayIcon  string
}

func (o ReplyAction) getType() string {
	return "reply"
}

func (o *ReplyAction) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

type AutomaticAction struct {
	Name  string
	Delay int
}

func (o AutomaticAction) getType() string {
	return "auto"
}

type ProgressBar struct {
	Title        string
	Subtitle     string
	Label        string
	Progress     int /* percentage */
	DisplayClass string
	DisplayIcon  string
}

func (o ProgressBar) getType() string {
	return "progressBar"
}

type Typed interface {
	getType() string
}

func walk(o interface{}) map[string]interface{} {

	m := make(map[string]interface{})

	if t, ok := o.(Typed); ok {
		m["type"] = t.getType()
	}

	val := reflect.ValueOf(o)

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		val := valueField.Interface()

		if val == nil {
			continue
		}

		valueField = reflect.ValueOf(val)

		if valueField.Kind() == reflect.Ptr && !isZero(valueField) {
			valueField = valueField.Elem()
			val = valueField.Interface()
		}

		switch valueField.Kind() {
		case reflect.Struct:
			val = walk(val)
		case reflect.Slice:
			vals := []interface{}{}
			for i := 0; i < valueField.Len(); i++ {
				if valueField.Index(i).Kind() == reflect.Interface || valueField.Index(i).Kind() == reflect.Struct {
					vals = append(vals, walk(valueField.Index(i).Interface()))
				} else {
					vals = append(vals, valueField.Index(i).Interface())
				}
				val = vals
			}
		default:
			if isZero(valueField) {
				val = nil
			}
		}

		if val != nil {
			m[lF(typeField.Name)] = val
		}
	}

	return m
}

func isZero(valueField reflect.Value) bool {
	return valueField.Interface() == reflect.Zero(valueField.Type()).Interface()
}

func lF(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

func (o *ConfigurationScreen) UnmarshalJSON(bytes []byte) error {
	aMap := make(map[string]interface{})
	if err := json.Unmarshal(bytes, &aMap); err != nil {
		return err
	}
	return hydrate(aMap, o)
}

func makeTyped(typeName string) (interface{}, error) {
	switch typeName {
	case "inputText":
		return &InputText{}, nil
	case "staticText":
		return &StaticText{}, nil
	case "inputTimeRange":
		return &InputTimeRange{}, nil
	case "optionGroup":
		return &OptionGroup{}, nil
	case "close":
		return &CloseAction{}, nil
	case "reply":
		return &ReplyAction{}, nil
	default:
		return nil, fmt.Errorf("can't make object for type: %s", typeName)
	}
}

func hydrate(s map[string]interface{}, o interface{}) error {
	v := reflect.ValueOf(o).Elem()
	switch v.Kind() {
	case reflect.Struct:
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			var mfv reflect.Value
			fv := v.Field(i)
			ft := t.Field(i)

			if sv, ok := s[lF(ft.Name)]; ok && sv != nil {
				mfv = reflect.ValueOf(sv)
				switch ft.Type.Kind() {
				case reflect.Struct:
					if svMap, ok := sv.(map[string]interface{}); !ok {
						return fmt.Errorf("failed to convert value to map")
					} else {
						if err := hydrate(svMap, fv.Addr().Interface()); err != nil {
							return fmt.Errorf("failed to hydrate %+v", ft)
						}
						continue
					}
				case reflect.Slice:
					if mfv.Kind() != reflect.Slice {
						return fmt.Errorf("hydrate: while processing '%s': failed to map '%v' to slice: value=%v", ft.Name, mfv.Kind(), fv)
					} else {
						if mfv.Type() != ft.Type {
							tmp := reflect.MakeSlice(ft.Type, mfv.Len(), mfv.Len())
							for j := 0; j < mfv.Len(); j++ {
								p := mfv.Index(j)
								vp := reflect.Indirect(p)
								vpMap := vp.Interface().(map[string]interface{})
								if ft.Type.Elem().Kind() == reflect.Interface {
									if typeName, ok := vpMap["type"].(string); ok {
										if typed, err := makeTyped(typeName); err != nil {
											return err
										} else {
											if err := hydrate(vpMap, typed); err != nil {
												return err
											}
											tmp.Index(j).Set(reflect.ValueOf(typed).Elem())
										}
									} else {
										return fmt.Errorf("trying to unmarshall interface, but no type available")
									}
								} else {
									if err := hydrate(vpMap, tmp.Index(j).Addr().Interface()); err != nil {
										return err
									}
								}
							}
							mfv = tmp
						}
					}
				case reflect.Ptr:
					mfv = mfv.Addr()
				}
			} else {
				mfv = reflect.Zero(ft.Type)
			}

			fv.Set(mfv.Convert(ft.Type))
		}
		return nil
	default:
		return fmt.Errorf("hydrate: unhandled kind: %v", v.Kind())
	}
}
<commit_msg>Add additional support for all Typed things.<commit_after>package suit

import (
	"encoding/json"
	"fmt"
	"reflect"
	"unicode"
	"unicode/utf8"
)

type ConfigurationScreen struct {
	Title       string
	Subtitle    string
	DisplayIcon string
	Sections    []Section
	Actions     []Typed
}

func (o *ConfigurationScreen) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

type Section struct {
	Title    string
	Subtitle string
	Contents []Typed
	Well     bool
}

type InputText struct {
	Title       string
	Subtitle    string
	Before      string
	After       string
	Placeholder string
	Name        string
	Value       interface{}
	InputType   string
	Minimum     *int
	Maximum     *int
}

func (o InputText) getType() string {
	return "inputText"
}

type StaticText struct {
	Title    string
	Subtitle string
	Before   string
	After    string
	Value    string
}

func (o StaticText) getType() string {
	return "staticText"
}

type InputTime struct {
	Title    string
	Subtitle string
	Before   string
	After    string
	Name     string
	Value    string
}

func (o InputTime) getType() string {
	return "inputTime"
}

type Separator struct {
}

func (o Separator) getType() string {
	return "separator"
}

type OptionGroup struct {
	Title          string
	Subtitle       string
	Name           string
	MinimumChoices int
	MaximumChoices int
	Options        []OptionGroupOption
}

func (o OptionGroup) getType() string {
	return "optionGroup"
}

type OptionGroupOption struct {
	Title    string
	Subtitle string
	Value    string
	Selected bool
}

type RadioGroup struct {
	Title    string
	Subtitle string
	Name     string
	Value    string
	Options  []RadioGroupOption
}

func (o RadioGroup) getType() string {
	return "radioGroup"
}

type RadioGroupOption struct {
	Title       string
	Value       string
	DisplayIcon string
}

type Alert struct {
	Title        string
	Subtitle     string
	DisplayClass string
	DisplayIcon  string
}

func (o Alert) getType() string {
	return "alert"
}

type ActionList struct {
	Title           string
	Subtitle        string
	Name            string
	Options         []ActionListOption
	PrimaryAction   Typed
	SecondaryAction Typed
}

func (o ActionList) getType() string {
	return "actionList"
}

type ActionListOption struct {
	Title    string
	Subtitle string
	Value    string
}

type InputTimeRange struct {
	Title    string
	Subtitle string
	Name     string
	Value    TimeRange
}

func (o InputTimeRange) getType() string {
	return "inputTimeRange"
}

type TimeRange struct {
	From string `json:"from"`
	To   string `json:"to"`
}

type InputHidden struct {
	Name  string
	Value string
}

func (o InputHidden) getType() string {
	return "inputHidden"
}

type CloseAction struct {
	Label string
}

func (o CloseAction) getType() string {
	return "close"
}

type ReplyAction struct {
	Label        string
	Name         string
	DisplayClass string
	DisplayIcon  string
}

func (o ReplyAction) getType() string {
	return "reply"
}

func (o *ReplyAction) MarshalJSON() ([]byte, error) {
	return json.Marshal(walk(*o))
}

type AutomaticAction struct {
	Name  string
	Delay int
}

func (o AutomaticAction) getType() string {
	return "auto"
}

type ProgressBar struct {
	Title        string
	Subtitle     string
	Label        string
	Progress     int /* percentage */
	DisplayClass string
	DisplayIcon  string
}

func (o ProgressBar) getType() string {
	return "progressBar"
}

type Typed interface {
	getType() string
}

func walk(o interface{}) map[string]interface{} {

	m := make(map[string]interface{})

	if t, ok := o.(Typed); ok {
		m["type"] = t.getType()
	}

	val := reflect.ValueOf(o)

	for i := 0; i < val.NumField(); i++ {
		valueField := val.Field(i)
		typeField := val.Type().Field(i)

		val := valueField.Interface()

		if val == nil {
			continue
		}

		valueField = reflect.ValueOf(val)

		if valueField.Kind() == reflect.Ptr && !isZero(valueField) {
			valueField = valueField.Elem()
			val = valueField.Interface()
		}

		switch valueField.Kind() {
		case reflect.Struct:
			val = walk(val)
		case reflect.Slice:
			vals := []interface{}{}
			for i := 0; i < valueField.Len(); i++ {
				if valueField.Index(i).Kind() == reflect.Interface || valueField.Index(i).Kind() == reflect.Struct {
					vals = append(vals, walk(valueField.Index(i).Interface()))
				} else {
					vals = append(vals, valueField.Index(i).Interface())
				}
				val = vals
			}
		default:
			if isZero(valueField) {
				val = nil
			}
		}

		if val != nil {
			m[lF(typeField.Name)] = val
		}
	}

	return m
}

func isZero(valueField reflect.Value) bool {
	return valueField.Interface() == reflect.Zero(valueField.Type()).Interface()
}

func lF(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}

func (o *ConfigurationScreen) UnmarshalJSON(bytes []byte) error {
	aMap := make(map[string]interface{})
	if err := json.Unmarshal(bytes, &aMap); err != nil {
		return err
	}
	return hydrate(aMap, o)
}

func makeTyped(typeName string) (interface{}, error) {
	switch typeName {
	case "actionList":
		return &ActionList{}, nil
	case "alert":
		return &Alert{}, nil
	case "auto":
		return &AutomaticAction{}, nil
	case "close":
		return &CloseAction{}, nil
	case "inputHidden":
		return &InputHidden{}, nil
	case "inputText":
		return &InputText{}, nil
	case "inputTime":
		return &InputTime{}, nil
	case "inputTimeRange":
		return &InputTimeRange{}, nil
	case "optionGroup":
		return &OptionGroup{}, nil
	case "progressBar":
		return &ProgressBar{}, nil
	case "radioGroup":
		return &RadioGroup{}, nil
	case "reply":
		return &ReplyAction{}, nil
	case "separator":
		return &Separator{}, nil
	case "staticText":
		return &StaticText{}, nil
	default:
		return nil, fmt.Errorf("can't make object for type: %s", typeName)
	}
}

func hydrate(s map[string]interface{}, o interface{}) error {
	v := reflect.ValueOf(o).Elem()
	switch v.Kind() {
	case reflect.Struct:
		t := v.Type()
		for i := 0; i < v.NumField(); i++ {
			var mfv reflect.Value
			fv := v.Field(i)
			ft := t.Field(i)

			if sv, ok := s[lF(ft.Name)]; ok && sv != nil {
				mfv = reflect.ValueOf(sv)
				switch ft.Type.Kind() {
				case reflect.Struct:
					if svMap, ok := sv.(map[string]interface{}); !ok {
						return fmt.Errorf("failed to convert value to map")
					} else {
						if err := hydrate(svMap, fv.Addr().Interface()); err != nil {
							return fmt.Errorf("failed to hydrate %+v", ft)
						}
						continue
					}
				case reflect.Slice:
					if mfv.Kind() != reflect.Slice {
						return fmt.Errorf("hydrate: while processing '%s': failed to map '%v' to slice: value=%v", ft.Name, mfv.Kind(), fv)
					} else {
						if mfv.Type() != ft.Type {
							tmp := reflect.MakeSlice(ft.Type, mfv.Len(), mfv.Len())
							for j := 0; j < mfv.Len(); j++ {
								p := mfv.Index(j)
								vp := reflect.Indirect(p)
								vpMap := vp.Interface().(map[string]interface{})
								if ft.Type.Elem().Kind() == reflect.Interface {
									if typeName, ok := vpMap["type"].(string); ok {
										if typed, err := makeTyped(typeName); err != nil {
											return err
										} else {
											if err := hydrate(vpMap, typed); err != nil {
												return err
											}
											tmp.Index(j).Set(reflect.ValueOf(typed).Elem())
										}
									} else {
										return fmt.Errorf("trying to unmarshall interface, but no type available")
									}
								} else {
									if err := hydrate(vpMap, tmp.Index(j).Addr().Interface()); err != nil {
										return err
									}
								}
							}
							mfv = tmp
						}
					}
				case reflect.Ptr:
					mfv = mfv.Addr()
				}
			} else {
				mfv = reflect.Zero(ft.Type)
			}

			fv.Set(mfv.Convert(ft.Type))
		}
		return nil
	default:
		return fmt.Errorf("hydrate: unhandled kind: %v", v.Kind())
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package suite allows to read tests and collections of tests (suites) from
// disk and execute them in a controlled way or run throughput load test from
// these test/suites.
//
package suite

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/vdobler/ht/cookiejar"
	"github.com/vdobler/ht/ht"
	"github.com/vdobler/ht/mock"
	"github.com/vdobler/ht/scope"
)

// A Suite is a collection of Tests which can be executed sequentily with the
// result captured.
type Suite struct {
	Name        string // Name of the Suite.
	Description string // Description of what's going on here.
	KeepCookies bool   // KeepCookies in a cookie jar common to all Tests.

	Status   ht.Status     // Status is the overall status of the whole suite.
	Error    error         // Error encountered during execution of the suite.
	Started  time.Time     // Start of the execution.
	Duration time.Duration // Duration of the execution.

	Tests []*ht.Test // The Tests to execute

	Variables      scope.Variables // The initial variable assignment
	FinalVariables scope.Variables // The final set of variables.
	Jar            *cookiejar.Jar  // The cookie jar used

	Verbosity int
	Log       interface {
		Printf(format string, a ...interface{})
	}

	globals          scope.Variables
	tests            []*RawTest
	noneTeardownTest int
}

func shouldRun(t int, rs *RawSuite, s *Suite) bool {
	if !rs.tests[t].IsEnabled() {
		return false
	}

	// Stop execution on errors during setup
	for i := 0; i < len(rs.Setup) && i < len(s.Tests); i++ {
		if s.Tests[i].Status > ht.Pass {
			return false
		}
	}
	return true
}

// NewFromRaw sets up a new Suite from rs, read to be Iterated.
func NewFromRaw(rs *RawSuite, global map[string]string, jar *cookiejar.Jar, logger *log.Logger) *Suite {
	// Create cookie jar if needed.
	if rs.KeepCookies {
		if jar == nil {
			// Make own, private-use jar.
			jar, _ = cookiejar.New(nil)
		}
	} else {
		jar = nil
	}

	if logger == nil {
		logger = log.New(ioutil.Discard, "", 0)
	}

	suite := &Suite{
		KeepCookies: rs.KeepCookies,

		Status: ht.NotRun,
		Error:  nil,

		Tests: make([]*ht.Test, 0, len(rs.tests)),

		Variables:        make(map[string]string),
		FinalVariables:   make(map[string]string),
		Jar:              jar,
		Log:              logger,
		Verbosity:        rs.Verbosity,
		tests:            rs.tests,
		noneTeardownTest: len(rs.Setup) + len(rs.Main),
	}

	suite.globals = scope.New(global, rs.Variables, true)
	suite.globals["SUITE_DIR"] = rs.File.Dirname()
	suite.globals["SUITE_NAME"] = rs.File.Basename()
	replacer := suite.globals.Replacer()

	suite.Name = replacer.Replace(rs.Name)
	suite.Description = replacer.Replace(rs.Description)

	for n, v := range suite.globals {
		suite.Variables[n] = v
	}

	return suite
}

// A Executor is responsible for executing the given test during the
// Iterate'ion of a Suite. It should return nil if execution should continue
// and ErrAbortExecution to stop further iteration.
type Executor func(test *ht.Test) error

var (
	// ErrAbortExecution indicates that suite iteration should stop.
	ErrAbortExecution = errors.New("Abort Execution")
)

var mockDelay = 50 * time.Millisecond

// Iterate the suite through the given executor.
func (suite *Suite) Iterate(executor Executor) {
	now := time.Now()
	now = now.Add(-time.Duration(now.Nanosecond()))
	suite.Started = now

	overall := ht.NotRun
	errors := ht.ErrorList{}

	for _, rt := range suite.tests {
		// suite.Log.Printf("Executing Test %q\n", rt.File.Name)
		callScope := scope.New(suite.globals, rt.contextVars, true)
		testScope := scope.New(callScope, rt.Variables, false)
		testScope["TEST_DIR"] = rt.File.Dirname()
		testScope["TEST_NAME"] = rt.File.Basename()
		test, err := rt.ToTest(testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}
		test.Jar = suite.Jar
		test.Log = suite.Log

		// Mocks requested for this test: We expect each mock to be
		// called exactly once (and this call should pass).
		var mockResult []*ht.Test
		stopMocks, monitor, monitoringDone, expect, err := startMocks(suite, test, rt, &mockResult, testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}

		// Execute the test (if not bogus).
		exstat := executor(test)

		if stopMocks != nil {
			// We got running mocks: Stop mock handling and stop monitoring
			stopMocks <- true
			<-stopMocks
			close(monitor)
			<-monitoringDone

			// Analyse what we got and updates test.
			analyseMocks(test, mockResult, expect)
		}
		if test.Status == ht.Pass {
			suite.updateVariables(test)
		}

		suite.Tests = append(suite.Tests, test)
		if test.Status > overall {
			overall = test.Status
		}
		if err := test.Error; err != nil {
			errors = append(errors, err)
		}

		if exstat == ErrAbortExecution {
			break
		}
	}
	suite.Duration = time.Since(suite.Started)
	clip := suite.Duration.Nanoseconds() % 1000000
	suite.Duration -= time.Duration(clip)
	suite.Status = overall
	if len(errors) == 0 {
		suite.Error = nil
	} else {
		suite.Error = errors
	}

	for n, v := range suite.globals {
		suite.FinalVariables[n] = v
	}
}

func startMocks(suite *Suite, test *ht.Test, rt *RawTest, mockResult *[]*ht.Test, testScope scope.Variables) (stopMocks chan bool, monitor chan *ht.Test, monitoringDone chan bool, expect []string, err error) {
	if len(rt.mocks) == 0 {
		return nil, nil, nil, nil, nil
	}

	monitor = make(chan *ht.Test)
	mocks := []*mock.Mock{}
	for i, m := range rt.mocks {
		mockScope := scope.New(testScope, rt.Variables, false)
		mockScope["MOCK_DIR"] = m.Dirname()
		mockScope["MOCK_NAME"] = m.Basename()
		mk, err := m.ToMock(mockScope, true)
		if err != nil {
			return nil, nil, nil, nil,
				fmt.Errorf("mock %d %q is malformed: %s",
					i+1, m.Name, err)

		}
		mk.Monitor = monitor
		mocks = append(mocks, mk)
		expect = append(expect, mk.Name)
	}

	// Report any calls that miss explicit mock handlers as 404.
	notFoundHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := ioutil.ReadAll(r.Body)
		u := r.URL.String()
		report := &ht.Test{
			Name: "Not Found " + u,
			Request: ht.Request{
				Method:   r.Method,
				URL:      u,
				Header:   r.Header,
				Request:  r,
				SentBody: string(body),
			},
		}
		http.Error(w, "No mock for "+u, http.StatusNotFound)
		monitor <- report
	})

	stopMocks, err = mock.Serve(mocks, notFoundHandler, suite.Log)
	if err != nil {
		return nil, nil, nil, nil, err
	}

	monitoringDone = make(chan bool)
	go func() {
		for report := range monitor {
			logMock(suite, report)
			*mockResult = append(*mockResult, report)
		}
		close(monitoringDone)
	}()
	time.Sleep(mockDelay) // I'm clueless...

	return stopMocks, monitor, monitoringDone, expect, nil
}

func analyseMocks(test *ht.Test, mockResult []*ht.Test, expect []string) {
	actual := []string{}
	for _, mt := range mockResult {
		actual = append(actual, mt.Name)
		// Propagete state of mock invocations to main test.
		if mt.Status > test.Status {
			test.Status = mt.Status
			test.Error = mt.Error
		}
		// Augment check name with information about mock.
		for i := range mt.CheckResults {
			name := mt.CheckResults[i].Name
			name = fmt.Sprintf("Request to mock %q: %s", mt.Name, name)
			mt.CheckResults[i].Name = name
		}
		// Append check results of mock to main test checks.
		test.CheckResults = append(test.CheckResults, mt.CheckResults...)
	}

	// Did we get exactly what we expected?
	sort.Strings(expect)
	sort.Strings(actual)
	// Sorry for that.
	want, got := strings.Join(expect, " ※ "), strings.Join(actual, " ※ ")
	if got != want && test.Status == ht.Pass {
		test.Status = ht.Fail
		test.Error = fmt.Errorf("Expected mocks [%s] but actual mock invocations were [%s]",
			want, got)
	}
}

func logMock(suite *Suite, report *ht.Test) {
	if suite.Verbosity <= 0 {
		return
	}
	if suite.Verbosity < 3 {
		suite.Log.Printf("Mock invoked %q: %s %s", report.Name,
			report.Request.Method, report.Request.URL)
	} else {
		suite.Log.Printf("%s", mock.PrintReport(report))
	}
}

func (suite *Suite) updateVariables(test *ht.Test) {
	if test.Status != ht.Pass {
		return
	}

	for varname, value := range test.Extract() {
		if suite.Verbosity >= 2 {
			if old, ok := suite.globals[varname]; ok {
				if value != old {
					suite.Log.Printf("Updating variable %q to %q\n",
						varname, value)
				} else {
					suite.Log.Printf("Keeping  variable %q as %q\n",
						varname, value)
				}
			} else {
				suite.Log.Printf("Setting  variable %q to %q\n",
					varname, value)
			}
		}

		suite.globals[varname] = value
	}
}

func (suite *Suite) updateStatus(test *ht.Test) {
	if test.Status <= suite.Status {
		return
	}

	suite.Status = test.Status
	if test.Error != nil {
		suite.Error = test.Error
	}
}

// Stats counts the test results of s.
func (suite *Suite) Stats() (notRun int, skipped int, passed int, failed int, errored int, bogus int) {
	for _, tr := range suite.Tests {
		switch tr.Status {
		case ht.NotRun:
			notRun++
		case ht.Skipped:
			skipped++
		case ht.Pass:
			passed++
		case ht.Fail:
			failed++
		case ht.Error:
			errored++
		case ht.Bogus:
			bogus++
		default:
			panic(fmt.Sprintf("No such Status %d in suite %q test %q",
				tr.Status, suite.Name, tr.Name))
		}
	}
	return
}
<commit_msg>suite: use new mock package<commit_after>// Copyright 2016 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package suite allows to read tests and collections of tests (suites) from
// disk and execute them in a controlled way or run throughput load test from
// these test/suites.
//
package suite

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/vdobler/ht/cookiejar"
	"github.com/vdobler/ht/ht"
	"github.com/vdobler/ht/mock"
	"github.com/vdobler/ht/scope"
)

// A Suite is a collection of Tests which can be executed sequentily with the
// result captured.
type Suite struct {
	Name        string // Name of the Suite.
	Description string // Description of what's going on here.
	KeepCookies bool   // KeepCookies in a cookie jar common to all Tests.

	Status   ht.Status     // Status is the overall status of the whole suite.
	Error    error         // Error encountered during execution of the suite.
	Started  time.Time     // Start of the execution.
	Duration time.Duration // Duration of the execution.

	Tests []*ht.Test // The Tests to execute

	Variables      scope.Variables // The initial variable assignment
	FinalVariables scope.Variables // The final set of variables.
	Jar            *cookiejar.Jar  // The cookie jar used

	Verbosity int
	Log       interface {
		Printf(format string, a ...interface{})
	}

	globals          scope.Variables
	tests            []*RawTest
	noneTeardownTest int
}

func shouldRun(t int, rs *RawSuite, s *Suite) bool {
	if !rs.tests[t].IsEnabled() {
		return false
	}

	// Stop execution on errors during setup
	for i := 0; i < len(rs.Setup) && i < len(s.Tests); i++ {
		if s.Tests[i].Status > ht.Pass {
			return false
		}
	}
	return true
}

// NewFromRaw sets up a new Suite from rs, read to be Iterated.
func NewFromRaw(rs *RawSuite, global map[string]string, jar *cookiejar.Jar, logger *log.Logger) *Suite {
	// Create cookie jar if needed.
	if rs.KeepCookies {
		if jar == nil {
			// Make own, private-use jar.
			jar, _ = cookiejar.New(nil)
		}
	} else {
		jar = nil
	}

	if logger == nil {
		logger = log.New(ioutil.Discard, "", 0)
	}

	suite := &Suite{
		KeepCookies: rs.KeepCookies,

		Status: ht.NotRun,
		Error:  nil,

		Tests: make([]*ht.Test, 0, len(rs.tests)),

		Variables:        make(map[string]string),
		FinalVariables:   make(map[string]string),
		Jar:              jar,
		Log:              logger,
		Verbosity:        rs.Verbosity,
		tests:            rs.tests,
		noneTeardownTest: len(rs.Setup) + len(rs.Main),
	}

	suite.globals = scope.New(global, rs.Variables, true)
	suite.globals["SUITE_DIR"] = rs.File.Dirname()
	suite.globals["SUITE_NAME"] = rs.File.Basename()
	replacer := suite.globals.Replacer()

	suite.Name = replacer.Replace(rs.Name)
	suite.Description = replacer.Replace(rs.Description)

	for n, v := range suite.globals {
		suite.Variables[n] = v
	}

	return suite
}

// A Executor is responsible for executing the given test during the
// Iterate'ion of a Suite. It should return nil if execution should continue
// and ErrAbortExecution to stop further iteration.
type Executor func(test *ht.Test) error

var (
	// ErrAbortExecution indicates that suite iteration should stop.
	ErrAbortExecution = errors.New("Abort Execution")
)

var mockDelay = 50 * time.Millisecond

// Iterate the suite through the given executor.
func (suite *Suite) Iterate(executor Executor) {
	now := time.Now()
	now = now.Add(-time.Duration(now.Nanosecond()))
	suite.Started = now

	overall := ht.NotRun
	errors := ht.ErrorList{}

	for _, rt := range suite.tests {
		// suite.Log.Printf("Executing Test %q\n", rt.File.Name)
		callScope := scope.New(suite.globals, rt.contextVars, true)
		testScope := scope.New(callScope, rt.Variables, false)
		testScope["TEST_DIR"] = rt.File.Dirname()
		testScope["TEST_NAME"] = rt.File.Basename()
		test, err := rt.ToTest(testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}
		test.Jar = suite.Jar
		test.Log = suite.Log

		// Mocks requested for this test: We expect each mock to be
		// called exactly once (and this call should pass).
		var mockResult []*ht.Test
		stopMocks, monitor, monitoringDone, expect, err := startMocks(suite, test, rt, &mockResult, testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}

		// Execute the test (if not bogus).
		exstat := executor(test)

		if stopMocks != nil {
			// We got running mocks: Stop mock handling and stop monitoring
			stopMocks <- true
			<-stopMocks
			close(monitor)
			<-monitoringDone

			// Analyse what we got and updates test.
			analyseMocks(test, mockResult, expect)
		}
		if test.Status == ht.Pass {
			suite.updateVariables(test)
		}

		suite.Tests = append(suite.Tests, test)
		if test.Status > overall {
			overall = test.Status
		}
		if err := test.Error; err != nil {
			errors = append(errors, err)
		}

		if exstat == ErrAbortExecution {
			break
		}
	}
	suite.Duration = time.Since(suite.Started)
	clip := suite.Duration.Nanoseconds() % 1000000
	suite.Duration -= time.Duration(clip)
	suite.Status = overall
	if len(errors) == 0 {
		suite.Error = nil
	} else {
		suite.Error = errors
	}

	for n, v := range suite.globals {
		suite.FinalVariables[n] = v
	}
}

func startMocks(suite *Suite, test *ht.Test, rt *RawTest, mockResult *[]*ht.Test, testScope scope.Variables) (stopMocks chan bool, monitor chan *ht.Test, monitoringDone chan bool, expect []string, err error) {
	if len(rt.mocks) == 0 {
		return nil, nil, nil, nil, nil
	}

	monitor = make(chan *ht.Test)
	mocks := []*mock.Mock{}
	for i, m := range rt.mocks {
		mockScope := scope.New(testScope, rt.Variables, false)
		mockScope["MOCK_DIR"] = m.Dirname()
		mockScope["MOCK_NAME"] = m.Basename()
		mk, err := m.ToMock(mockScope, true)
		if err != nil {
			return nil, nil, nil, nil,
				fmt.Errorf("mock %d %q is malformed: %s",
					i+1, m.Name, err)

		}
		mk.Monitor = monitor
		mocks = append(mocks, mk)
		expect = append(expect, mk.Name)
	}

	// Report any calls that miss explicit mock handlers as 404.
	notFoundHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := ioutil.ReadAll(r.Body)
		u := r.URL.String()
		report := &ht.Test{
			Name: "Not Found " + u,
			Request: ht.Request{
				Method:   r.Method,
				URL:      u,
				Header:   r.Header,
				Request:  r,
				SentBody: string(body),
			},
		}
		http.Error(w, "No mock for "+u, http.StatusNotFound)
		monitor <- report
	})

	stopMocks, err = mock.Serve(mocks, notFoundHandler, suite.Log, "", "")
	if err != nil {
		return nil, nil, nil, nil, err
	}

	monitoringDone = make(chan bool)
	go func() {
		for report := range monitor {
			logMock(suite, report)
			*mockResult = append(*mockResult, report)
		}
		close(monitoringDone)
	}()
	time.Sleep(mockDelay) // I'm clueless...

	return stopMocks, monitor, monitoringDone, expect, nil
}

func analyseMocks(test *ht.Test, mockResult []*ht.Test, expect []string) {
	actual := []string{}
	for _, mt := range mockResult {
		actual = append(actual, mt.Name)
		// Propagete state of mock invocations to main test.
		if mt.Status > test.Status {
			test.Status = mt.Status
			test.Error = mt.Error
		}
		// Augment check name with information about mock.
		for i := range mt.CheckResults {
			name := mt.CheckResults[i].Name
			name = fmt.Sprintf("Request to mock %q: %s", mt.Name, name)
			mt.CheckResults[i].Name = name
		}
		// Append check results of mock to main test checks.
		test.CheckResults = append(test.CheckResults, mt.CheckResults...)
	}

	// Did we get exactly what we expected?
	sort.Strings(expect)
	sort.Strings(actual)
	// Sorry for that.
	want, got := strings.Join(expect, " ※ "), strings.Join(actual, " ※ ")
	if got != want && test.Status == ht.Pass {
		test.Status = ht.Fail
		test.Error = fmt.Errorf("Expected mocks [%s] but actual mock invocations were [%s]",
			want, got)
	}
}

func logMock(suite *Suite, report *ht.Test) {
	if suite.Verbosity <= 0 {
		return
	}
	if suite.Verbosity < 3 {
		suite.Log.Printf("Mock invoked %q: %s %s", report.Name,
			report.Request.Method, report.Request.URL)
	} else {
		suite.Log.Printf("%s", mock.PrintReport(report))
	}
}

func (suite *Suite) updateVariables(test *ht.Test) {
	if test.Status != ht.Pass {
		return
	}

	for varname, value := range test.Extract() {
		if suite.Verbosity >= 2 {
			if old, ok := suite.globals[varname]; ok {
				if value != old {
					suite.Log.Printf("Updating variable %q to %q\n",
						varname, value)
				} else {
					suite.Log.Printf("Keeping  variable %q as %q\n",
						varname, value)
				}
			} else {
				suite.Log.Printf("Setting  variable %q to %q\n",
					varname, value)
			}
		}

		suite.globals[varname] = value
	}
}

func (suite *Suite) updateStatus(test *ht.Test) {
	if test.Status <= suite.Status {
		return
	}

	suite.Status = test.Status
	if test.Error != nil {
		suite.Error = test.Error
	}
}

// Stats counts the test results of s.
func (suite *Suite) Stats() (notRun int, skipped int, passed int, failed int, errored int, bogus int) {
	for _, tr := range suite.Tests {
		switch tr.Status {
		case ht.NotRun:
			notRun++
		case ht.Skipped:
			skipped++
		case ht.Pass:
			passed++
		case ht.Fail:
			failed++
		case ht.Error:
			errored++
		case ht.Bogus:
			bogus++
		default:
			panic(fmt.Sprintf("No such Status %d in suite %q test %q",
				tr.Status, suite.Name, tr.Name))
		}
	}
	return
}
<|endoftext|>


<commit_before>package log

import (
	"errors"
	"fmt"
	"github.com/astaxie/beego/logs"
)

func Prepare() error {
	progressLog = new(logs.BeeLogger)
	return nil
}

func LoadConsole(cfg Config) (err error) {
	err = progressLog.SetLogger(adapterConsole, cfg.ToJson())
	if err != nil {
		err = errors.New("load console error when set logger:" + err.Error())
		return
	}
	// 日志总开关
	progressLog.SetLevel(LevelDebug)
	err = progressLog.DelLogger(logs.AdapterConsole)
	if err != nil {
		err = errors.New("load console error when del logger:" + err.Error())
	}
	return
}

func LoadFileLogger(cfg Config) (err error) {
	if len(cfg.Filename) > 0 {
		err = progressLog.SetLogger(logs.AdapterFile, cfg.ToJson())
		if err != nil {
			err = fmt.Errorf("set file logger error:%v", err)
		}
	}

	if !cfg.EnableStdout {
		if dErr := progressLog.DelLogger(adapterConsole); dErr != nil {
			WarningF("disable stdout error:%v", dErr)
		}
	}
	return
}
<commit_msg>print log file<commit_after>package log

import (
	"errors"
	"fmt"
	"github.com/astaxie/beego/logs"
)

func Prepare() error {
	progressLog = new(logs.BeeLogger)
	return nil
}

func LoadConsole(cfg Config) (err error) {
	err = progressLog.SetLogger(adapterConsole, cfg.ToJson())
	if err != nil {
		err = errors.New("load console error when set logger:" + err.Error())
		return
	}
	// 日志总开关
	progressLog.SetLevel(LevelDebug)
	err = progressLog.DelLogger(logs.AdapterConsole)
	if err != nil {
		err = errors.New("load console error when del logger:" + err.Error())
	}
	return
}

func LoadFileLogger(cfg Config) (err error) {
	if len(cfg.Filename) > 0 {
		err = progressLog.SetLogger(logs.AdapterFile, cfg.ToJson())
		if err != nil {
			err = fmt.Errorf("set file logger error:%v", err)
		}
		DebugF("log file:%s", cfg.Filename)
	}

	if !cfg.EnableStdout {
		if dErr := progressLog.DelLogger(adapterConsole); dErr != nil {
			WarningF("disable stdout error:%v", dErr)
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package testbed

import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"math/rand"
	"strconv"
	"time"

	"go.uber.org/atomic"

	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/consumer/pdatautil"
	otlptrace "go.opentelemetry.io/collector/internal/data/opentelemetry-proto-gen/trace/v1"
	"go.opentelemetry.io/collector/internal/dataold"
	"go.opentelemetry.io/collector/internal/goldendataset"
)

// DataProvider defines the interface for generators of test data used to drive various end-to-end tests.
type DataProvider interface {
	// SetLoadGeneratorCounters supplies pointers to LoadGenerator counters.
	// The data provider implementation should increment these as it generates data.
	SetLoadGeneratorCounters(batchesGenerated *atomic.Uint64, dataItemsGenerated *atomic.Uint64)
	// GenerateTraces returns an internal Traces instance with an OTLP ResourceSpans slice populated with test data.
	GenerateTraces() (pdata.Traces, bool)
	// GenerateMetrics returns an internal MetricData instance with an OTLP ResourceMetrics slice of test data.
	GenerateMetrics() (pdata.Metrics, bool)
	// GetGeneratedSpan returns the generated Span matching the provided traceId and spanId or else nil if no match found.
	GetGeneratedSpan(traceID []byte, spanID []byte) *otlptrace.Span
	// GenerateLogs returns the internal pdata.Logs format
	GenerateLogs() (pdata.Logs, bool)
}

// PerfTestDataProvider in an implementation of the DataProvider for use in performance tests.
// Tracing IDs are based on the incremented batch and data items counters.
type PerfTestDataProvider struct {
	options            LoadOptions
	batchesGenerated   *atomic.Uint64
	dataItemsGenerated *atomic.Uint64
}

// NewPerfTestDataProvider creates an instance of PerfTestDataProvider which generates test data based on the sizes
// specified in the supplied LoadOptions.
func NewPerfTestDataProvider(options LoadOptions) *PerfTestDataProvider {
	return &PerfTestDataProvider{
		options: options,
	}
}

func (dp *PerfTestDataProvider) SetLoadGeneratorCounters(batchesGenerated *atomic.Uint64, dataItemsGenerated *atomic.Uint64) {
	dp.batchesGenerated = batchesGenerated
	dp.dataItemsGenerated = dataItemsGenerated
}

func (dp *PerfTestDataProvider) GenerateTraces() (pdata.Traces, bool) {

	traceData := pdata.NewTraces()
	traceData.ResourceSpans().Resize(1)
	ilss := traceData.ResourceSpans().At(0).InstrumentationLibrarySpans()
	ilss.Resize(1)
	spans := ilss.At(0).Spans()
	spans.Resize(dp.options.ItemsPerBatch)

	traceID := dp.batchesGenerated.Inc()
	for i := 0; i < dp.options.ItemsPerBatch; i++ {

		startTime := time.Now()
		endTime := startTime.Add(time.Millisecond)

		spanID := dp.dataItemsGenerated.Inc()

		span := spans.At(i)

		// Create a span.
		span.SetTraceID(GenerateSequentialTraceID(traceID))
		span.SetSpanID(GenerateSequentialSpanID(spanID))
		span.SetName("load-generator-span")
		span.SetKind(pdata.SpanKindCLIENT)
		attrs := span.Attributes()
		attrs.UpsertInt("load_generator.span_seq_num", int64(spanID))
		attrs.UpsertInt("load_generator.trace_seq_num", int64(traceID))
		// Additional attributes.
		for k, v := range dp.options.Attributes {
			attrs.UpsertString(k, v)
		}
		span.SetStartTime(pdata.TimestampUnixNano(uint64(startTime.UnixNano())))
		span.SetEndTime(pdata.TimestampUnixNano(uint64(endTime.UnixNano())))
	}
	return traceData, false
}

func GenerateSequentialTraceID(id uint64) []byte {
	var traceID [16]byte
	binary.PutUvarint(traceID[:], id)
	return traceID[:]
}

func GenerateSequentialSpanID(id uint64) []byte {
	var spanID [8]byte
	binary.PutUvarint(spanID[:], id)
	return spanID[:]
}

func (dp *PerfTestDataProvider) GenerateMetrics() (pdata.Metrics, bool) {

	// Generate 7 data points per metric.
	const dataPointsPerMetric = 7

	metricData := dataold.NewMetricData()
	metricData.ResourceMetrics().Resize(1)
	metricData.ResourceMetrics().At(0).InstrumentationLibraryMetrics().Resize(1)
	if dp.options.Attributes != nil {
		attrs := metricData.ResourceMetrics().At(0).Resource().Attributes()
		attrs.InitEmptyWithCapacity(len(dp.options.Attributes))
		for k, v := range dp.options.Attributes {
			attrs.UpsertString(k, v)
		}
	}
	metrics := metricData.ResourceMetrics().At(0).InstrumentationLibraryMetrics().At(0).Metrics()
	metrics.Resize(dp.options.ItemsPerBatch)

	for i := 0; i < dp.options.ItemsPerBatch; i++ {
		metric := metrics.At(i)
		metricDescriptor := metric.MetricDescriptor()
		metricDescriptor.InitEmpty()
		metricDescriptor.SetName("load_generator_" + strconv.Itoa(i))
		metricDescriptor.SetDescription("Load Generator Counter #" + strconv.Itoa(i))
		metricDescriptor.SetType(dataold.MetricTypeInt64)

		batchIndex := dp.batchesGenerated.Inc()

		// Generate data points for the metric.
		metric.Int64DataPoints().Resize(dataPointsPerMetric)
		for j := 0; j < dataPointsPerMetric; j++ {
			dataPoint := metric.Int64DataPoints().At(j)
			dataPoint.SetStartTime(pdata.TimestampUnixNano(uint64(time.Now().UnixNano())))
			value := dp.dataItemsGenerated.Inc()
			dataPoint.SetValue(int64(value))
			dataPoint.LabelsMap().InitFromMap(map[string]string{
				"item_index":  "item_" + strconv.Itoa(j),
				"batch_index": "batch_" + strconv.Itoa(int(batchIndex)),
			})
		}
	}
	return pdatautil.MetricsFromOldInternalMetrics(metricData), false
}

func (dp *PerfTestDataProvider) GetGeneratedSpan([]byte, []byte) *otlptrace.Span {
	// function not supported for this data provider
	return nil
}

func (dp *PerfTestDataProvider) GenerateLogs() (pdata.Logs, bool) {
	logs := pdata.NewLogs()
	logs.ResourceLogs().Resize(1)
	logs.ResourceLogs().At(0).InstrumentationLibraryLogs().Resize(1)
	if dp.options.Attributes != nil {
		attrs := logs.ResourceLogs().At(0).Resource().Attributes()
		attrs.InitEmptyWithCapacity(len(dp.options.Attributes))
		for k, v := range dp.options.Attributes {
			attrs.UpsertString(k, v)
		}
	}
	logRecords := logs.ResourceLogs().At(0).InstrumentationLibraryLogs().At(0).Logs()
	logRecords.Resize(dp.options.ItemsPerBatch)

	now := pdata.TimestampUnixNano(time.Now().UnixNano())

	batchIndex := dp.batchesGenerated.Inc()

	for i := 0; i < dp.options.ItemsPerBatch; i++ {
		itemIndex := dp.dataItemsGenerated.Inc()
		record := logRecords.At(i)
		record.InitEmpty()
		record.SetSeverityNumber(pdata.SeverityNumberINFO3)
		record.SetSeverityText("INFO3")
		record.SetName("load_generator_" + strconv.Itoa(i))
		record.Body().SetStringVal("Load Generator Counter #" + strconv.Itoa(i))
		record.SetFlags(uint32(2))
		record.SetTimestamp(now)

		attrs := record.Attributes()
		attrs.UpsertString("batch_index", "batch_"+strconv.Itoa(int(batchIndex)))
		attrs.UpsertString("item_index", "item_"+strconv.Itoa(int(itemIndex)))
		attrs.UpsertString("a", "test")
		attrs.UpsertDouble("b", 5.0)
		attrs.UpsertInt("c", 3)
		attrs.UpsertBool("d", true)
	}
	return logs, false
}

// GoldenDataProvider is an implementation of DataProvider for use in correctness tests.
// Provided data from the "Golden" dataset generated using pairwise combinatorial testing techniques.
type GoldenDataProvider struct {
	tracePairsFile     string
	spanPairsFile      string
	random             io.Reader
	batchesGenerated   *atomic.Uint64
	dataItemsGenerated *atomic.Uint64
	resourceSpans      []*otlptrace.ResourceSpans
	spansIndex         int
	spansMap           map[string]*otlptrace.Span
}

// NewGoldenDataProvider creates a new instance of GoldenDataProvider which generates test data based
// on the pairwise combinations specified in the tracePairsFile and spanPairsFile input variables.
// The supplied randomSeed is used to initialize the random number generator used in generating tracing IDs.
func NewGoldenDataProvider(tracePairsFile string, spanPairsFile string, randomSeed int64) *GoldenDataProvider {
	return &GoldenDataProvider{
		tracePairsFile: tracePairsFile,
		spanPairsFile:  spanPairsFile,
		random:         io.Reader(rand.New(rand.NewSource(randomSeed))),
	}
}

func (dp *GoldenDataProvider) SetLoadGeneratorCounters(batchesGenerated *atomic.Uint64, dataItemsGenerated *atomic.Uint64) {
	dp.batchesGenerated = batchesGenerated
	dp.dataItemsGenerated = dataItemsGenerated
}

func (dp *GoldenDataProvider) GenerateTraces() (pdata.Traces, bool) {
	if dp.resourceSpans == nil {
		var err error
		dp.resourceSpans, err = goldendataset.GenerateResourceSpans(dp.tracePairsFile, dp.spanPairsFile, dp.random)
		if err != nil {
			log.Printf("cannot generate traces: %s", err)
			dp.resourceSpans = make([]*otlptrace.ResourceSpans, 0)
		}
	}
	dp.batchesGenerated.Inc()
	if dp.spansIndex >= len(dp.resourceSpans) {
		return pdata.TracesFromOtlp(make([]*otlptrace.ResourceSpans, 0)), true
	}
	resourceSpans := make([]*otlptrace.ResourceSpans, 1)
	resourceSpans[0] = dp.resourceSpans[dp.spansIndex]
	dp.spansIndex++
	spanCount := uint64(0)
	for _, libSpans := range resourceSpans[0].InstrumentationLibrarySpans {
		spanCount += uint64(len(libSpans.Spans))
	}
	dp.dataItemsGenerated.Add(spanCount)
	return pdata.TracesFromOtlp(resourceSpans), false
}

func (dp *GoldenDataProvider) GenerateMetrics() (pdata.Metrics, bool) {
	return pdatautil.MetricsFromOldInternalMetrics(dataold.MetricData{}), true
}

func (dp *GoldenDataProvider) GenerateLogs() (pdata.Logs, bool) {
	return pdata.Logs{}, true
}

func (dp *GoldenDataProvider) GetGeneratedSpan(traceID []byte, spanID []byte) *otlptrace.Span {
	if dp.spansMap == nil {
		dp.spansMap = populateSpansMap(dp.resourceSpans)
	}
	key := traceIDAndSpanIDToString(traceID, spanID)
	return dp.spansMap[key]
}

func populateSpansMap(resourceSpansList []*otlptrace.ResourceSpans) map[string]*otlptrace.Span {
	spansMap := make(map[string]*otlptrace.Span)
	for _, resourceSpans := range resourceSpansList {
		for _, libSpans := range resourceSpans.InstrumentationLibrarySpans {
			for _, span := range libSpans.Spans {
				key := traceIDAndSpanIDToString(span.TraceId, span.SpanId)
				spansMap[key] = span
			}
		}
	}
	return spansMap
}

func traceIDAndSpanIDToString(traceID []byte, spanID []byte) string {
	return fmt.Sprintf("%s-%s", hex.EncodeToString(traceID), hex.EncodeToString(spanID))
}
<commit_msg>Change testbed to use the new internal metrics structs (#1698)<commit_after>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package testbed

import (
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"math/rand"
	"strconv"
	"time"

	"go.uber.org/atomic"

	"go.opentelemetry.io/collector/consumer/pdata"
	"go.opentelemetry.io/collector/consumer/pdatautil"
	"go.opentelemetry.io/collector/internal/data"
	otlptrace "go.opentelemetry.io/collector/internal/data/opentelemetry-proto-gen/trace/v1"
	"go.opentelemetry.io/collector/internal/goldendataset"
)

// DataProvider defines the interface for generators of test data used to drive various end-to-end tests.
type DataProvider interface {
	// SetLoadGeneratorCounters supplies pointers to LoadGenerator counters.
	// The data provider implementation should increment these as it generates data.
	SetLoadGeneratorCounters(batchesGenerated *atomic.Uint64, dataItemsGenerated *atomic.Uint64)
	// GenerateTraces returns an internal Traces instance with an OTLP ResourceSpans slice populated with test data.
	GenerateTraces() (pdata.Traces, bool)
	// GenerateMetrics returns an internal MetricData instance with an OTLP ResourceMetrics slice of test data.
	GenerateMetrics() (pdata.Metrics, bool)
	// GetGeneratedSpan returns the generated Span matching the provided traceId and spanId or else nil if no match found.
	GetGeneratedSpan(traceID []byte, spanID []byte) *otlptrace.Span
	// GenerateLogs returns the internal pdata.Logs format
	GenerateLogs() (pdata.Logs, bool)
}

// PerfTestDataProvider in an implementation of the DataProvider for use in performance tests.
// Tracing IDs are based on the incremented batch and data items counters.
type PerfTestDataProvider struct {
	options            LoadOptions
	batchesGenerated   *atomic.Uint64
	dataItemsGenerated *atomic.Uint64
}

// NewPerfTestDataProvider creates an instance of PerfTestDataProvider which generates test data based on the sizes
// specified in the supplied LoadOptions.
func NewPerfTestDataProvider(options LoadOptions) *PerfTestDataProvider {
	return &PerfTestDataProvider{
		options: options,
	}
}

func (dp *PerfTestDataProvider) SetLoadGeneratorCounters(batchesGenerated *atomic.Uint64, dataItemsGenerated *atomic.Uint64) {
	dp.batchesGenerated = batchesGenerated
	dp.dataItemsGenerated = dataItemsGenerated
}

func (dp *PerfTestDataProvider) GenerateTraces() (pdata.Traces, bool) {

	traceData := pdata.NewTraces()
	traceData.ResourceSpans().Resize(1)
	ilss := traceData.ResourceSpans().At(0).InstrumentationLibrarySpans()
	ilss.Resize(1)
	spans := ilss.At(0).Spans()
	spans.Resize(dp.options.ItemsPerBatch)

	traceID := dp.batchesGenerated.Inc()
	for i := 0; i < dp.options.ItemsPerBatch; i++ {

		startTime := time.Now()
		endTime := startTime.Add(time.Millisecond)

		spanID := dp.dataItemsGenerated.Inc()

		span := spans.At(i)

		// Create a span.
		span.SetTraceID(GenerateSequentialTraceID(traceID))
		span.SetSpanID(GenerateSequentialSpanID(spanID))
		span.SetName("load-generator-span")
		span.SetKind(pdata.SpanKindCLIENT)
		attrs := span.Attributes()
		attrs.UpsertInt("load_generator.span_seq_num", int64(spanID))
		attrs.UpsertInt("load_generator.trace_seq_num", int64(traceID))
		// Additional attributes.
		for k, v := range dp.options.Attributes {
			attrs.UpsertString(k, v)
		}
		span.SetStartTime(pdata.TimestampUnixNano(uint64(startTime.UnixNano())))
		span.SetEndTime(pdata.TimestampUnixNano(uint64(endTime.UnixNano())))
	}
	return traceData, false
}

func GenerateSequentialTraceID(id uint64) []byte {
	var traceID [16]byte
	binary.PutUvarint(traceID[:], id)
	return traceID[:]
}

func GenerateSequentialSpanID(id uint64) []byte {
	var spanID [8]byte
	binary.PutUvarint(spanID[:], id)
	return spanID[:]
}

func (dp *PerfTestDataProvider) GenerateMetrics() (pdata.Metrics, bool) {

	// Generate 7 data points per metric.
	const dataPointsPerMetric = 7

	metricData := data.NewMetricData()
	metricData.ResourceMetrics().Resize(1)
	metricData.ResourceMetrics().At(0).InstrumentationLibraryMetrics().Resize(1)
	if dp.options.Attributes != nil {
		attrs := metricData.ResourceMetrics().At(0).Resource().Attributes()
		attrs.InitEmptyWithCapacity(len(dp.options.Attributes))
		for k, v := range dp.options.Attributes {
			attrs.UpsertString(k, v)
		}
	}
	metrics := metricData.ResourceMetrics().At(0).InstrumentationLibraryMetrics().At(0).Metrics()
	metrics.Resize(dp.options.ItemsPerBatch)

	for i := 0; i < dp.options.ItemsPerBatch; i++ {
		metric := metrics.At(i)
		metric.SetName("load_generator_" + strconv.Itoa(i))
		metric.SetDescription("Load Generator Counter #" + strconv.Itoa(i))
		metric.SetUnit("1")
		metric.SetDataType(pdata.MetricDataTypeIntGauge)
		gauge := metric.IntGauge()
		gauge.InitEmpty()

		batchIndex := dp.batchesGenerated.Inc()

		// Generate data points for the metric.
		gauge.DataPoints().Resize(dataPointsPerMetric)
		for j := 0; j < dataPointsPerMetric; j++ {
			dataPoint := gauge.DataPoints().At(j)
			dataPoint.SetStartTime(pdata.TimestampUnixNano(uint64(time.Now().UnixNano())))
			value := dp.dataItemsGenerated.Inc()
			dataPoint.SetValue(int64(value))
			dataPoint.LabelsMap().InitFromMap(map[string]string{
				"item_index":  "item_" + strconv.Itoa(j),
				"batch_index": "batch_" + strconv.Itoa(int(batchIndex)),
			})
		}
	}
	return pdatautil.MetricsFromInternalMetrics(metricData), false
}

func (dp *PerfTestDataProvider) GetGeneratedSpan([]byte, []byte) *otlptrace.Span {
	// function not supported for this data provider
	return nil
}

func (dp *PerfTestDataProvider) GenerateLogs() (pdata.Logs, bool) {
	logs := pdata.NewLogs()
	logs.ResourceLogs().Resize(1)
	logs.ResourceLogs().At(0).InstrumentationLibraryLogs().Resize(1)
	if dp.options.Attributes != nil {
		attrs := logs.ResourceLogs().At(0).Resource().Attributes()
		attrs.InitEmptyWithCapacity(len(dp.options.Attributes))
		for k, v := range dp.options.Attributes {
			attrs.UpsertString(k, v)
		}
	}
	logRecords := logs.ResourceLogs().At(0).InstrumentationLibraryLogs().At(0).Logs()
	logRecords.Resize(dp.options.ItemsPerBatch)

	now := pdata.TimestampUnixNano(time.Now().UnixNano())

	batchIndex := dp.batchesGenerated.Inc()

	for i := 0; i < dp.options.ItemsPerBatch; i++ {
		itemIndex := dp.dataItemsGenerated.Inc()
		record := logRecords.At(i)
		record.InitEmpty()
		record.SetSeverityNumber(pdata.SeverityNumberINFO3)
		record.SetSeverityText("INFO3")
		record.SetName("load_generator_" + strconv.Itoa(i))
		record.Body().SetStringVal("Load Generator Counter #" + strconv.Itoa(i))
		record.SetFlags(uint32(2))
		record.SetTimestamp(now)

		attrs := record.Attributes()
		attrs.UpsertString("batch_index", "batch_"+strconv.Itoa(int(batchIndex)))
		attrs.UpsertString("item_index", "item_"+strconv.Itoa(int(itemIndex)))
		attrs.UpsertString("a", "test")
		attrs.UpsertDouble("b", 5.0)
		attrs.UpsertInt("c", 3)
		attrs.UpsertBool("d", true)
	}
	return logs, false
}

// GoldenDataProvider is an implementation of DataProvider for use in correctness tests.
// Provided data from the "Golden" dataset generated using pairwise combinatorial testing techniques.
type GoldenDataProvider struct {
	tracePairsFile     string
	spanPairsFile      string
	random             io.Reader
	batchesGenerated   *atomic.Uint64
	dataItemsGenerated *atomic.Uint64
	resourceSpans      []*otlptrace.ResourceSpans
	spansIndex         int
	spansMap           map[string]*otlptrace.Span
}

// NewGoldenDataProvider creates a new instance of GoldenDataProvider which generates test data based
// on the pairwise combinations specified in the tracePairsFile and spanPairsFile input variables.
// The supplied randomSeed is used to initialize the random number generator used in generating tracing IDs.
func NewGoldenDataProvider(tracePairsFile string, spanPairsFile string, randomSeed int64) *GoldenDataProvider {
	return &GoldenDataProvider{
		tracePairsFile: tracePairsFile,
		spanPairsFile:  spanPairsFile,
		random:         io.Reader(rand.New(rand.NewSource(randomSeed))),
	}
}

func (dp *GoldenDataProvider) SetLoadGeneratorCounters(batchesGenerated *atomic.Uint64, dataItemsGenerated *atomic.Uint64) {
	dp.batchesGenerated = batchesGenerated
	dp.dataItemsGenerated = dataItemsGenerated
}

func (dp *GoldenDataProvider) GenerateTraces() (pdata.Traces, bool) {
	if dp.resourceSpans == nil {
		var err error
		dp.resourceSpans, err = goldendataset.GenerateResourceSpans(dp.tracePairsFile, dp.spanPairsFile, dp.random)
		if err != nil {
			log.Printf("cannot generate traces: %s", err)
			dp.resourceSpans = make([]*otlptrace.ResourceSpans, 0)
		}
	}
	dp.batchesGenerated.Inc()
	if dp.spansIndex >= len(dp.resourceSpans) {
		return pdata.TracesFromOtlp(make([]*otlptrace.ResourceSpans, 0)), true
	}
	resourceSpans := make([]*otlptrace.ResourceSpans, 1)
	resourceSpans[0] = dp.resourceSpans[dp.spansIndex]
	dp.spansIndex++
	spanCount := uint64(0)
	for _, libSpans := range resourceSpans[0].InstrumentationLibrarySpans {
		spanCount += uint64(len(libSpans.Spans))
	}
	dp.dataItemsGenerated.Add(spanCount)
	return pdata.TracesFromOtlp(resourceSpans), false
}

func (dp *GoldenDataProvider) GenerateMetrics() (pdata.Metrics, bool) {
	return pdatautil.MetricsFromInternalMetrics(data.MetricData{}), true
}

func (dp *GoldenDataProvider) GenerateLogs() (pdata.Logs, bool) {
	return pdata.Logs{}, true
}

func (dp *GoldenDataProvider) GetGeneratedSpan(traceID []byte, spanID []byte) *otlptrace.Span {
	if dp.spansMap == nil {
		dp.spansMap = populateSpansMap(dp.resourceSpans)
	}
	key := traceIDAndSpanIDToString(traceID, spanID)
	return dp.spansMap[key]
}

func populateSpansMap(resourceSpansList []*otlptrace.ResourceSpans) map[string]*otlptrace.Span {
	spansMap := make(map[string]*otlptrace.Span)
	for _, resourceSpans := range resourceSpansList {
		for _, libSpans := range resourceSpans.InstrumentationLibrarySpans {
			for _, span := range libSpans.Spans {
				key := traceIDAndSpanIDToString(span.TraceId, span.SpanId)
				spansMap[key] = span
			}
		}
	}
	return spansMap
}

func traceIDAndSpanIDToString(traceID []byte, spanID []byte) string {
	return fmt.Sprintf("%s-%s", hex.EncodeToString(traceID), hex.EncodeToString(spanID))
}
<|endoftext|>