<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"testing"
	"time"
)

const (
	ANY_DB_DRIVER     = ""
	MULTI_DOMAIN_MODE = ""
	TEST_DEPLOYED_ON  = "localhost:9999"
	TEST_DB           = "goslow_test"
)

var DATA_SOURCE = map[string]string{
	ANY_DB_DRIVER: "", // value doesn't matter
	"sqlite3":     DEFAULT_CONFIG.dataSource,
	"postgres":    "postgres://localhost/" + TEST_DB,
}

type TestCase struct {
	driver                 string
	dataSource             string
	createDefaultEndpoints bool
	adminPathPrefix        string
}

type TestCases []*TestCase

// TODO: do we need *TestCase argument in ServerTest?
type ServerTest func(*testing.T, *httptest.Server, *TestCase)

var (
	multiDomainTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
	}

	endpointCreationTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
		NewTestCase(ANY_DB_DRIVER, "/goslow"),
		NewTestCase(ANY_DB_DRIVER, "/goslow/"),
		NewTestCase(ANY_DB_DRIVER, "/te"),
		NewTestCase(ANY_DB_DRIVER, "/te/"),
		NewTestCase(ANY_DB_DRIVER, "/composite/path"),
	}
)

func NewTestCase(driver string, adminPathPrefix string) *TestCase {
	dataSource, knownDriver := DATA_SOURCE[driver]
	if !knownDriver {
		log.Fatalf("unknown driver: <%s>", driver)
	}
	createDefaultEndpoints := (adminPathPrefix == MULTI_DOMAIN_MODE)
	return &TestCase{
		driver:                 driver,
		dataSource:             dataSource,
		createDefaultEndpoints: createDefaultEndpoints,
		adminPathPrefix:        adminPathPrefix,
	}
}

func TestZeroSite(t *testing.T) {
	runAll(t, zeroSiteServerTest, multiDomainTestCases)
}

func runAll(t *testing.T, serverTest ServerTest, testCases TestCases) {
	for _, testCase := range runnable(expandToConcreteDriver(testCases)) {
		run(t, serverTest, testCase)
	}
}

func runnable(testCases TestCases) TestCases {
	runnableTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.runnable() {
			runnableTestCases = append(runnableTestCases, testCase)
		}
	}
	return runnableTestCases
}

func expandToConcreteDriver(testCases TestCases) TestCases {
	concreteTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.driver == ANY_DB_DRIVER {
			concreteTestCases = append(concreteTestCases, NewTestCase("sqlite3", testCase.adminPathPrefix))
			concreteTestCases = append(concreteTestCases, NewTestCase("postgres", testCase.adminPathPrefix))
		} else {
			concreteTestCases = append(concreteTestCases, testCase)
		}
	}
	return concreteTestCases
}

func (testCase *TestCase) runnable() bool {
	return !testCase.skippable()
}

func (testCase *TestCase) skippable() bool {
	return testCase.driver == "postgres" && testing.Short()
}

func run(t *testing.T, serverTest ServerTest, testCase *TestCase) {
	if testCase.driver == "postgres" {
		createDb(TEST_DB)
		defer dropDb(TEST_DB)
	}
	goSlowServer := newGoSlowServer(testCase)
	server := httptest.NewServer(goSlowServer)
	defer server.Close()
	defer goSlowServer.storage.db.Close() // so we can drop database
	serverTest(t, server, testCase)
}

func zeroSiteServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondWith(t, DEFAULT_RESPONSE,
		createGET(server.URL, "/", makeFullDomain("0")))
}

func shouldRespondWith(t *testing.T, expectedResponse []byte, request *http.Request) {
	response := do(request)
	bytesShouldBeEqual(t, expectedResponse, read(response))
}

func TestTooLargeDelay(t *testing.T) {
	runAll(t, tooLargeDelayServerTest, multiDomainTestCases)
}

func tooLargeDelayServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	dontAllowToCreateEndpointWithDelay(t, server, time.Duration(1000)*time.Second)
}

func dontAllowToCreateEndpointWithDelay(t *testing.T, server *httptest.Server, delay time.Duration) {
	withNewDomain(server, func(domain string) {
		resp := createEndpoint(server, &Endpoint{Site: getSite(domain), Path: "/", Delay: delay})

		shouldHaveStatusCode(t, http.StatusBadRequest, resp)
		shouldHaveStatusCode(t, http.StatusNotFound, GET(server.URL, "/", domain))
	})
}

type DomainTest func(domain string)

func withNewDomain(server *httptest.Server, domainTest DomainTest) {
	domain := createDomain(server, &Endpoint{Path: "/path-is-irrelevant"})
	domainTest(domain)
}

func TestChangeBuiltinSites(t *testing.T) {
	runAll(t, changeBuiltinSitesServerTest, multiDomainTestCases)
}

// TODO: test admin-599 sites
func changeBuiltinSitesServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	dontAllowToChangeSite(t, server, "0")
	dontAllowToChangeSite(t, server, "599")
	dontAllowToChangeSite(t, server, "create")
}

func dontAllowToChangeSite(t *testing.T, server *httptest.Server, site string) {
	resp := createEndpoint(server, &Endpoint{Site: site, Path: "/test", Response: []byte("hop"), Method: "GET"})
	shouldHaveStatusCode(t, http.StatusForbidden, resp)
}

func TestRedefineNonExistentSite(t *testing.T) {
	runAll(t, checkRedefineNonExistentSite, multiDomainTestCases)
}

func checkRedefineNonExistentSite(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, site := range []string{"", "ha", "admin-500"} {
		resp := createEndpoint(server, &Endpoint{Site: site})
		shouldHaveStatusCode(t, http.StatusNotFound, resp)
	}
}

func TestDelay(t *testing.T) {
	runAll(t, checkDelay, multiDomainTestCases)
}

func checkDelay(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("0")),
		0, 0.1) // seconds
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("1")),
		1, 1.1) // seconds
}

func TestStatus(t *testing.T) {
	runAll(t, checkStatus, multiDomainTestCases)
}

func checkStatus(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, statusCode := range []int{200, 404, 500} {
		resp := GET(server.URL, "/", makeFullDomain(strconv.Itoa(statusCode)))
		shouldHaveStatusCode(t, statusCode, resp)
	}
}

func TestEndpointCreation(t *testing.T) {
	runAll(t, checkEndpointCreationTestCase, endpointCreationTestCases)
}

func checkEndpointCreationTestCase(t *testing.T, server *httptest.Server, testCase *TestCase) {
	prefix := testCase.adminPathPrefix
	isInSingleSiteMode := prefix != ""
	domain, site := TEST_DEPLOYED_ON, ""
	root_response := []byte("haha")
	test_response := []byte("hop")
	test_post_response := []byte("for POST")
	empty_payload := []byte("")

	if isInSingleSiteMode {
		resp := createEndpoint(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		shouldHaveStatusCode(t, http.StatusOK, resp)
	} else {
		domain = createDomain(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		site = getSite(domain)
	}

	bytesShouldBeEqual(t, read(GET(server.URL, "/", domain)), root_response)

	// testing GET endpoint
	resp := createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_response, Method: "GET"})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that GET /test endpoint works
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that GET /test doesn't affect POST
	resp = POST(server.URL, "/test", domain, []byte(""))
	intShouldBeEqual(t, 404, resp.StatusCode)

	// testing POST endpoint
	resp = createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_post_response, Method: "POST",
		Delay: time.Duration(100) * time.Millisecond})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that POST endpoint doesn't affect GET
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that POST /test endpoint works
	bytesShouldBeEqual(t, read(POST(server.URL, "/test", domain, empty_payload)), test_post_response)
	shouldRespondIn(t, createPOST(server.URL, "/test", domain, empty_payload), 0.1, 0.15)
}

func newGoSlowServer(testCase *TestCase) *Server {
	config := DEFAULT_CONFIG // copies DEFAULT_CONFIG
	config.deployedOn = TEST_DEPLOYED_ON
	config.createDefaultEndpoints = testCase.createDefaultEndpoints
	config.adminPathPrefix = testCase.adminPathPrefix
	config.driver = testCase.driver
	config.dataSource = testCase.dataSource
	return NewServer(&config)
}

func GET(url, path, host string) *http.Response {
	req := createGET(url, path, host)
	return do(req)
}

func do(req *http.Request) *http.Response {
	resp, err := new(http.Client).Do(req)
	if err != nil {
		log.Fatal(err)
	}
	return resp
}

func createGET(url, path, host string) *http.Request {
	return createRequest("GET", url, path, host, nil)
}

func createRequest(method, url, path, host string, body io.Reader) *http.Request {
	req, err := http.NewRequest(method, url+path, body)
	if err != nil {
		log.Fatal(err)
	}
	req.Host = host
	return req
}

func read(resp *http.Response) []byte {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	return body
}

func makeFullDomain(site string) string {
	return fmt.Sprintf("%s.%s", site, TEST_DEPLOYED_ON)
}

func bytesShouldBeEqual(t *testing.T, expected, actual []byte) {
	if !bytes.Equal(expected, actual) {
		t.Fatalf("<<%v>> != <<%v>>", string(expected), string(actual))
	}
}

func intShouldBeEqual(t *testing.T, expected, actual int) {
	if expected != actual {
		t.Fatalf("<<%v>> != <<%v>>", expected, actual)
	}
}

func shouldRespondIn(t *testing.T, req *http.Request, minSeconds, maxSeconds float64) {
	start := time.Now()
	resp := do(req)
	read(resp)
	duration := time.Since(start)
	minDuration := toDuration(minSeconds)
	maxDuration := toDuration(maxSeconds)
	if duration < minDuration || duration > maxDuration {
		t.Fatalf("%s%s answered in %v. Not in the interval [%v; %v]",
			req.Host, req.URL.Path, duration, minDuration, maxDuration)
	}
}

func shouldHaveStatusCode(t *testing.T, statusCode int, resp *http.Response) {
	intShouldBeEqual(t, statusCode, resp.StatusCode)
}

func toDuration(seconds float64) time.Duration {
	return time.Duration(seconds*1000) * time.Millisecond
}

func createDomain(server *httptest.Server, endpoint *Endpoint) string {
	resp := POST(server.URL, fmt.Sprintf("%s?output=short&method=GET", endpoint.Path),
		makeFullDomain("create"), endpoint.Response)
	return string(read(resp))
}

func POST(url, path, host string, payload []byte) *http.Response {
	req := createPOST(url, path, host, payload)
	return do(req)
}

func createPOST(url, path, host string, payload []byte) *http.Request {
	return createRequest("POST", url, path, host, bytes.NewReader(payload))
}

func getSite(domain string) string {
	return strings.Split(domain, ".")[0]
}

func createEndpoint(server *httptest.Server, endpoint *Endpoint) *http.Response {
	req := createPOST(server.URL, endpoint.Path, makeFullDomain("admin-"+endpoint.Site),
		endpoint.Response)
	req.URL.RawQuery = getQueryString(endpoint)
	return do(req)
}

func getQueryString(endpoint *Endpoint) string {
	params := url.Values{}
	params.Set("method", endpoint.Method)
	params.Set("delay", fmt.Sprintf("%f", endpoint.Delay.Seconds()))
	return params.Encode()
}

// Wrapper around path.Join. Preserves trailing slash.
func join(elem ...string) string {
	lastElem := elem[len(elem)-1]
	shouldEndWithSlash := strings.HasSuffix(lastElem, "/")
	joined := path.Join(elem...)
	if shouldEndWithSlash && !strings.HasSuffix(joined, "/") {
		joined += "/"
	}
	return joined
}

func createDb(name string) {
	cmd := exec.Command("createdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("createdb error: %s", err)
	}
}

func dropDb(name string) {
	cmd := exec.Command("dropdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("dropdb error: %s", err)
	}
}
<commit_msg>Improve TestChangeUnknownSites<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os/exec"
	"path"
	"strconv"
	"strings"
	"testing"
	"time"
)

const (
	ANY_DB_DRIVER     = ""
	MULTI_DOMAIN_MODE = ""
	TEST_DEPLOYED_ON  = "localhost:9999"
	TEST_DB           = "goslow_test"
)

var DATA_SOURCE = map[string]string{
	ANY_DB_DRIVER: "", // value doesn't matter
	"sqlite3":     DEFAULT_CONFIG.dataSource,
	"postgres":    "postgres://localhost/" + TEST_DB,
}

type TestCase struct {
	driver                 string
	dataSource             string
	createDefaultEndpoints bool
	adminPathPrefix        string
}

type TestCases []*TestCase

// TODO: do we need *TestCase argument in ServerTest?
type ServerTest func(*testing.T, *httptest.Server, *TestCase)

var (
	multiDomainTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
	}

	endpointCreationTestCases = TestCases{
		NewTestCase(ANY_DB_DRIVER, MULTI_DOMAIN_MODE),
		NewTestCase(ANY_DB_DRIVER, "/goslow"),
		NewTestCase(ANY_DB_DRIVER, "/goslow/"),
		NewTestCase(ANY_DB_DRIVER, "/te"),
		NewTestCase(ANY_DB_DRIVER, "/te/"),
		NewTestCase(ANY_DB_DRIVER, "/composite/path"),
	}
)

func NewTestCase(driver string, adminPathPrefix string) *TestCase {
	dataSource, knownDriver := DATA_SOURCE[driver]
	if !knownDriver {
		log.Fatalf("unknown driver: <%s>", driver)
	}
	createDefaultEndpoints := (adminPathPrefix == MULTI_DOMAIN_MODE)
	return &TestCase{
		driver:                 driver,
		dataSource:             dataSource,
		createDefaultEndpoints: createDefaultEndpoints,
		adminPathPrefix:        adminPathPrefix,
	}
}

func TestZeroSite(t *testing.T) {
	runAll(t, zeroSiteServerTest, multiDomainTestCases)
}

func runAll(t *testing.T, serverTest ServerTest, testCases TestCases) {
	for _, testCase := range runnable(expandToConcreteDriver(testCases)) {
		run(t, serverTest, testCase)
	}
}

func runnable(testCases TestCases) TestCases {
	runnableTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.runnable() {
			runnableTestCases = append(runnableTestCases, testCase)
		}
	}
	return runnableTestCases
}

func expandToConcreteDriver(testCases TestCases) TestCases {
	concreteTestCases := make(TestCases, 0)
	for _, testCase := range testCases {
		if testCase.driver == ANY_DB_DRIVER {
			concreteTestCases = append(concreteTestCases, NewTestCase("sqlite3", testCase.adminPathPrefix))
			concreteTestCases = append(concreteTestCases, NewTestCase("postgres", testCase.adminPathPrefix))
		} else {
			concreteTestCases = append(concreteTestCases, testCase)
		}
	}
	return concreteTestCases
}

func (testCase *TestCase) runnable() bool {
	return !testCase.skippable()
}

func (testCase *TestCase) skippable() bool {
	return testCase.driver == "postgres" && testing.Short()
}

func run(t *testing.T, serverTest ServerTest, testCase *TestCase) {
	if testCase.driver == "postgres" {
		createDb(TEST_DB)
		defer dropDb(TEST_DB)
	}
	goSlowServer := newGoSlowServer(testCase)
	server := httptest.NewServer(goSlowServer)
	defer server.Close()
	defer goSlowServer.storage.db.Close() // so we can drop database
	serverTest(t, server, testCase)
}

func zeroSiteServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondWith(t, DEFAULT_RESPONSE,
		createGET(server.URL, "/", makeFullDomain("0")))
}

func shouldRespondWith(t *testing.T, expectedResponse []byte, request *http.Request) {
	response := do(request)
	bytesShouldBeEqual(t, expectedResponse, read(response))
}

func TestTooLargeDelay(t *testing.T) {
	runAll(t, tooLargeDelayServerTest, multiDomainTestCases)
}

func tooLargeDelayServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	dontAllowToCreateEndpointWithDelay(t, server, time.Duration(1000)*time.Second)
}

func dontAllowToCreateEndpointWithDelay(t *testing.T, server *httptest.Server, delay time.Duration) {
	withNewDomain(server, func(domain string) {
		resp := createEndpoint(server, &Endpoint{Site: getSite(domain), Path: "/", Delay: delay})

		shouldHaveStatusCode(t, http.StatusBadRequest, resp)
		shouldHaveStatusCode(t, http.StatusNotFound, GET(server.URL, "/", domain))
	})
}

type DomainTest func(domain string)

func withNewDomain(server *httptest.Server, domainTest DomainTest) {
	domain := createDomain(server, &Endpoint{Path: "/path-is-irrelevant"})
	domainTest(domain)
}

func TestChangeBuiltinSites(t *testing.T) {
	runAll(t, changeBuiltinSitesServerTest, multiDomainTestCases)
}

func changeBuiltinSitesServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	dontAllowToChangeSite(t, server, "0", http.StatusForbidden)
	dontAllowToChangeSite(t, server, "599", http.StatusForbidden)
	dontAllowToChangeSite(t, server, "create", http.StatusForbidden)
}

func dontAllowToChangeSite(t *testing.T, server *httptest.Server, site string, expectedStatusCode int) {
	resp := createEndpoint(server, &Endpoint{Site: site, Path: "/test", Response: []byte("hop"), Method: "GET"})
	shouldHaveStatusCode(t, expectedStatusCode, resp)
}

func TestChangeUnknownSites(t *testing.T) {
	runAll(t, changeUnknownSitesServerTest, multiDomainTestCases)
}

func changeUnknownSitesServerTest(t *testing.T, server *httptest.Server, testCase *TestCase) {
	dontAllowToChangeSite(t, server, "", http.StatusNotFound)
	dontAllowToChangeSite(t, server, "uknown-site", http.StatusNotFound)
	dontAllowToChangeSite(t, server, "admin-500", http.StatusNotFound)
	dontAllowToChangeSite(t, server, "admin-create", http.StatusNotFound)
}

func TestDelay(t *testing.T) {
	runAll(t, checkDelay, multiDomainTestCases)
}

func checkDelay(t *testing.T, server *httptest.Server, testCase *TestCase) {
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("0")),
		0, 0.1) // seconds
	shouldRespondIn(t,
		createGET(server.URL, "/", makeFullDomain("1")),
		1, 1.1) // seconds
}

func TestStatus(t *testing.T) {
	runAll(t, checkStatus, multiDomainTestCases)
}

func checkStatus(t *testing.T, server *httptest.Server, testCase *TestCase) {
	for _, statusCode := range []int{200, 404, 500} {
		resp := GET(server.URL, "/", makeFullDomain(strconv.Itoa(statusCode)))
		shouldHaveStatusCode(t, statusCode, resp)
	}
}

func TestEndpointCreation(t *testing.T) {
	runAll(t, checkEndpointCreationTestCase, endpointCreationTestCases)
}

func checkEndpointCreationTestCase(t *testing.T, server *httptest.Server, testCase *TestCase) {
	prefix := testCase.adminPathPrefix
	isInSingleSiteMode := prefix != ""
	domain, site := TEST_DEPLOYED_ON, ""
	root_response := []byte("haha")
	test_response := []byte("hop")
	test_post_response := []byte("for POST")
	empty_payload := []byte("")

	if isInSingleSiteMode {
		resp := createEndpoint(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		shouldHaveStatusCode(t, http.StatusOK, resp)
	} else {
		domain = createDomain(server, &Endpoint{Path: join(prefix, "/"), Response: root_response})
		site = getSite(domain)
	}

	bytesShouldBeEqual(t, read(GET(server.URL, "/", domain)), root_response)

	// testing GET endpoint
	resp := createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_response, Method: "GET"})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that GET /test endpoint works
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that GET /test doesn't affect POST
	resp = POST(server.URL, "/test", domain, []byte(""))
	intShouldBeEqual(t, 404, resp.StatusCode)

	// testing POST endpoint
	resp = createEndpoint(server, &Endpoint{Site: site, Path: join(prefix, "/test"), Response: test_post_response, Method: "POST",
		Delay: time.Duration(100) * time.Millisecond})
	shouldHaveStatusCode(t, http.StatusOK, resp)
	// checking that POST endpoint doesn't affect GET
	bytesShouldBeEqual(t, read(GET(server.URL, "/test", domain)), test_response)
	// checking that POST /test endpoint works
	bytesShouldBeEqual(t, read(POST(server.URL, "/test", domain, empty_payload)), test_post_response)
	shouldRespondIn(t, createPOST(server.URL, "/test", domain, empty_payload), 0.1, 0.15)
}

func newGoSlowServer(testCase *TestCase) *Server {
	config := DEFAULT_CONFIG // copies DEFAULT_CONFIG
	config.deployedOn = TEST_DEPLOYED_ON
	config.createDefaultEndpoints = testCase.createDefaultEndpoints
	config.adminPathPrefix = testCase.adminPathPrefix
	config.driver = testCase.driver
	config.dataSource = testCase.dataSource
	return NewServer(&config)
}

func GET(url, path, host string) *http.Response {
	req := createGET(url, path, host)
	return do(req)
}

func do(req *http.Request) *http.Response {
	resp, err := new(http.Client).Do(req)
	if err != nil {
		log.Fatal(err)
	}
	return resp
}

func createGET(url, path, host string) *http.Request {
	return createRequest("GET", url, path, host, nil)
}

func createRequest(method, url, path, host string, body io.Reader) *http.Request {
	req, err := http.NewRequest(method, url+path, body)
	if err != nil {
		log.Fatal(err)
	}
	req.Host = host
	return req
}

func read(resp *http.Response) []byte {
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}
	return body
}

func makeFullDomain(site string) string {
	return fmt.Sprintf("%s.%s", site, TEST_DEPLOYED_ON)
}

func bytesShouldBeEqual(t *testing.T, expected, actual []byte) {
	if !bytes.Equal(expected, actual) {
		t.Fatalf("<<%v>> != <<%v>>", string(expected), string(actual))
	}
}

func intShouldBeEqual(t *testing.T, expected, actual int) {
	if expected != actual {
		t.Fatalf("<<%v>> != <<%v>>", expected, actual)
	}
}

func shouldRespondIn(t *testing.T, req *http.Request, minSeconds, maxSeconds float64) {
	start := time.Now()
	resp := do(req)
	read(resp)
	duration := time.Since(start)
	minDuration := toDuration(minSeconds)
	maxDuration := toDuration(maxSeconds)
	if duration < minDuration || duration > maxDuration {
		t.Fatalf("%s%s answered in %v. Not in the interval [%v; %v]",
			req.Host, req.URL.Path, duration, minDuration, maxDuration)
	}
}

func shouldHaveStatusCode(t *testing.T, statusCode int, resp *http.Response) {
	intShouldBeEqual(t, statusCode, resp.StatusCode)
}

func toDuration(seconds float64) time.Duration {
	return time.Duration(seconds*1000) * time.Millisecond
}

func createDomain(server *httptest.Server, endpoint *Endpoint) string {
	resp := POST(server.URL, fmt.Sprintf("%s?output=short&method=GET", endpoint.Path),
		makeFullDomain("create"), endpoint.Response)
	return string(read(resp))
}

func POST(url, path, host string, payload []byte) *http.Response {
	req := createPOST(url, path, host, payload)
	return do(req)
}

func createPOST(url, path, host string, payload []byte) *http.Request {
	return createRequest("POST", url, path, host, bytes.NewReader(payload))
}

func getSite(domain string) string {
	return strings.Split(domain, ".")[0]
}

func createEndpoint(server *httptest.Server, endpoint *Endpoint) *http.Response {
	req := createPOST(server.URL, endpoint.Path, makeFullDomain("admin-"+endpoint.Site),
		endpoint.Response)
	req.URL.RawQuery = getQueryString(endpoint)
	return do(req)
}

func getQueryString(endpoint *Endpoint) string {
	params := url.Values{}
	params.Set("method", endpoint.Method)
	params.Set("delay", fmt.Sprintf("%f", endpoint.Delay.Seconds()))
	return params.Encode()
}

// Wrapper around path.Join. Preserves trailing slash.
func join(elem ...string) string {
	lastElem := elem[len(elem)-1]
	shouldEndWithSlash := strings.HasSuffix(lastElem, "/")
	joined := path.Join(elem...)
	if shouldEndWithSlash && !strings.HasSuffix(joined, "/") {
		joined += "/"
	}
	return joined
}

func createDb(name string) {
	cmd := exec.Command("createdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("createdb error: %s", err)
	}
}

func dropDb(name string) {
	cmd := exec.Command("dropdb", name)
	err := cmd.Run()
	if err != nil {
		log.Fatalf("dropdb error: %s", err)
	}
}
<|endoftext|>


<commit_before>//go:build pgonline || chartdata
// +build pgonline chartdata

package dcrpg

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"runtime"
	"testing"

	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrdata/v7/db/cache"
	"github.com/decred/dcrdata/v7/testutil/dbconfig"
	"github.com/decred/slog"
)

type MemStats runtime.MemStats

func memStats() MemStats {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	return MemStats(m)
}

func (m MemStats) String() string {
	bytesToMebiBytes := func(b uint64) uint64 {
		return b >> 20
	}
	out := "Memory statistics:\n"
	out += fmt.Sprintf("- Alloc: %v MiB\n", bytesToMebiBytes(m.Alloc))
	out += fmt.Sprintf("- TotalAlloc: %v MiB\n", bytesToMebiBytes(m.TotalAlloc))
	out += fmt.Sprintf("- System obtained: %v MiB\n", bytesToMebiBytes(m.Sys))
	out += fmt.Sprintf("- Lookups: %v\n", m.Lookups)
	out += fmt.Sprintf("- Mallocs: %v\n", m.Mallocs)
	out += fmt.Sprintf("- Frees: %v\n", m.Frees)
	out += fmt.Sprintf("- HeapAlloc: %v MiB\n", bytesToMebiBytes(m.HeapAlloc))
	out += fmt.Sprintf("- HeapObjects: %v\n", m.HeapObjects)
	out += fmt.Sprintf("- NumGC: %v\n", m.NumGC)
	return out
}

var (
	db           *ChainDB
	sqlDb        *sql.DB
	trefUNIX     int64 = 1454954400 // mainnet genesis block time
	trefStr            = "2016-02-08T12:00:00-06:00"
	addrCacheCap int   = 1e4
)

type dummyParser struct{}

func (p *dummyParser) UpdateSignal() <-chan struct{} {
	return make(chan struct{})
}

func openDB() (func() error, error) {
	dbi := &DBInfo{
		Host:   dbconfig.PGTestsHost,
		Port:   dbconfig.PGTestsPort,
		User:   dbconfig.PGTestsUser,
		Pass:   dbconfig.PGTestsPass,
		DBName: dbconfig.PGTestsDBName, // dcrdata_testnet3 for treasury testing
	}
	cfg := &ChainDBCfg{
		dbi,
		chaincfg.MainNetParams(),
		true, false, 24, 1024, 1 << 16,
	}
	var err error
	db, err = NewChainDB(context.Background(), cfg, nil, nil, new(dummyParser), nil, func() {})
	cleanUp := func() error { return nil }
	if db != nil {
		cleanUp = db.Close
	} else {
		return cleanUp, err
	}

	sqlDb = db.db
	if err = DropTestingTable(sqlDb); err != nil {
		return nil, err
	}
	if err = CreateTable(sqlDb, "testing"); err != nil {
		return nil, err
	}

	return cleanUp, err
}

func TestMain(m *testing.M) {
	// Setup dcrpg logger.
	UseLogger(slog.NewBackend(os.Stdout).Logger("dcrpg"))
	log.SetLevel(slog.LevelTrace)

	// Setup db/cache logger.
	cache.UseLogger(slog.NewBackend(os.Stdout).Logger("db/cache"))

	// your func
	cleanUp, err := openDB()
	defer cleanUp()
	if err != nil {
		panic(fmt.Sprintln("no db for testing:", err))
	}

	retCode := m.Run()

	// call with result of m.Run()
	os.Exit(retCode)
}
<commit_msg>dcrpg: fix pgonline and chartdata tests<commit_after>//go:build pgonline || chartdata
// +build pgonline chartdata

package dcrpg

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"runtime"
	"testing"

	"github.com/decred/dcrd/chaincfg/v3"
	"github.com/decred/dcrdata/v7/db/cache"
	"github.com/decred/dcrdata/v7/testutil/dbconfig"
	"github.com/decred/slog"
)

type MemStats runtime.MemStats

func memStats() MemStats {
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	return MemStats(m)
}

func (m MemStats) String() string {
	bytesToMebiBytes := func(b uint64) uint64 {
		return b >> 20
	}
	out := "Memory statistics:\n"
	out += fmt.Sprintf("- Alloc: %v MiB\n", bytesToMebiBytes(m.Alloc))
	out += fmt.Sprintf("- TotalAlloc: %v MiB\n", bytesToMebiBytes(m.TotalAlloc))
	out += fmt.Sprintf("- System obtained: %v MiB\n", bytesToMebiBytes(m.Sys))
	out += fmt.Sprintf("- Lookups: %v\n", m.Lookups)
	out += fmt.Sprintf("- Mallocs: %v\n", m.Mallocs)
	out += fmt.Sprintf("- Frees: %v\n", m.Frees)
	out += fmt.Sprintf("- HeapAlloc: %v MiB\n", bytesToMebiBytes(m.HeapAlloc))
	out += fmt.Sprintf("- HeapObjects: %v\n", m.HeapObjects)
	out += fmt.Sprintf("- NumGC: %v\n", m.NumGC)
	return out
}

var (
	db           *ChainDB
	sqlDb        *sql.DB
	trefUNIX     int64 = 1454954400 // mainnet genesis block time
	trefStr            = "2016-02-08T12:00:00-06:00"
	addrCacheCap int   = 1e4
)

func openDB() (func() error, error) {
	dbi := &DBInfo{
		Host:   dbconfig.PGTestsHost,
		Port:   dbconfig.PGTestsPort,
		User:   dbconfig.PGTestsUser,
		Pass:   dbconfig.PGTestsPass,
		DBName: dbconfig.PGTestsDBName, // dcrdata_testnet3 for treasury testing
	}
	cfg := &ChainDBCfg{
		dbi,
		chaincfg.MainNetParams(),
		true, false, 24, 1024, 1 << 16,
	}
	var err error
	db, err = NewChainDB(context.Background(), cfg, nil, nil, nil, func() {})
	cleanUp := func() error { return nil }
	if db != nil {
		cleanUp = db.Close
	} else {
		return cleanUp, err
	}

	sqlDb = db.db
	if err = DropTestingTable(sqlDb); err != nil {
		return nil, err
	}
	if err = CreateTable(sqlDb, "testing"); err != nil {
		return nil, err
	}

	return cleanUp, err
}

func TestMain(m *testing.M) {
	// Setup dcrpg logger.
	UseLogger(slog.NewBackend(os.Stdout).Logger("dcrpg"))
	log.SetLevel(slog.LevelTrace)

	// Setup db/cache logger.
	cache.UseLogger(slog.NewBackend(os.Stdout).Logger("db/cache"))

	// your func
	cleanUp, err := openDB()
	defer cleanUp()
	if err != nil {
		panic(fmt.Sprintln("no db for testing:", err))
	}

	retCode := m.Run()

	// call with result of m.Run()
	os.Exit(retCode)
}
<|endoftext|>


<commit_before>package main

import "flag"
import "os"
import "log"
import "fmt"
import "time"
import "strings"
import "regexp"
import "io/ioutil"

var options struct {
	show []string
}

func argParse() []string {
	var show string

	flag.StringVar(&show, "show", "", "log lines to show")

	flag.Parse()

	options.show = strings.Split(show, ",")
	args := flag.Args()
	if len(args) == 0 {
		log.Fatalf("Please specify a log file!!")
	}
	// validate whether log files are preset
	for _, logfile := range args {
		if _, err := os.Stat(logfile); err != nil {
			log.Fatal(err)
		}
	}
	return args
}

func main() {
	args := argParse()
	analyseLog(args[0])
}

func analyseLog(logfile string) {
	var msg *LogMsg
	var skips []string

	lines := readLines(logfile)
	msgs := make([]*LogMsg, 0)
	skipped := make([]string, 0)
	for len(lines) > 0 {
		msg, skips, lines = newLogMsg(lines)
		if !msg.isEmpty() {
			msgs = append(msgs, msg)
		}
		skipped = append(skipped, skips...)
	}
	fmt.Printf("Number of messages: %d\n", len(msgs))
	fmt.Printf("Lines skipped: %d\n", len(skipped))
	for _, show := range options.show {
		switch show {
		case "skip":
			listLines(skipped)
		}
	}
}

// log line
type Line string

func (l Line) time() time.Time {
	t, err := time.Parse(time.StampMicro, string(l))
	if err != nil {
		log.Fatal(err)
	}
	return t
}

func makefilter(all, any []*regexp.Regexp, pass bool) func(string) bool {
	return func(line string) bool {
		if all != nil {
			for _, re := range all {
				b := re.Match([]byte(line))
				if (pass && !b) || (!pass && b) {
					return false
				}
			}
			return true
		}
		for _, re := range any {
			b := re.Match([]byte(line))
			if (pass && b) || (!pass && !b) {
				return true
			}
		}
		return false
	}
}

func filter(fn func(string) bool, lines []string) []string {
	filtered := []string{}
	for _, line := range lines {
		if fn(line) {
			filtered = append(filtered, line)
		}
	}
	return filtered
}

func partition(fn func(string) bool, lines []string) ([]string, []string) {
	this, that := []string{}, []string{}
	for _, line := range lines {
		if fn(line) {
			this = append(this, line)
		} else {
			that = append(that, line)
		}
	}
	return this, that
}

func readLines(file string) []string {
	s, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal(err)
	}
	return strings.Split(string(s), "\n")
}

func listLines(lines []string) {
	for _, line := range lines {
		fmt.Println(line)
	}
}

// Validation on log lines.

func validate(lines []string) {
	validatePRAMRegistration(lines)
}

func validatePRAMRegistration(lines []string) {
	re, err := regexp.Compile(
		`registered.*` +
			`(/adminport/vbmapRequest|` +
			`/adminport/failoverLogRequest|` +
			`/adminport/mutationTopicRequest|` +
			`/adminport/restartVbucketsRequest|` +
			`/adminport/shutdownVbucketsRequest|` +
			`/adminport/addBucketsRequest|` +
			`/adminport/delBucketsRequest|` +
			`/adminport/addInstancesRequest|` +
			`/adminport/delInstancesRequest|` +
			`/adminport/repairEndpointsRequest|` +
			`/adminport/shutdownTopicRequest|` +
			`/adminport/stats|` +
			`/stats|` +
			`/settings)`)
	if err != nil {
		log.Fatal(err)
	}
	count := 0
	for _, line := range lines {
		if re.Match([]byte(line)) {
			count++
		}
	}
	if count != 14 {
		log.Fatalf("validaterPRAMRegistration(): %v", count)
	}
}

// parse feeds

type FeedLines struct {
	name      string
	crashed   []string
	stales    []string
	fatals    []string
	errors    []string
	warns     []string
	engines   []string
	endpoints map[string][]string // endpid -> lines
	byopaque  map[string][]string // opaque -> lines
}

var re_feed, _ = regexp.Compile(` FEED\[<=>([^\(]*)(`)
var re_feedcrashed, _ = regexp.Compile(` FEED\[<=>[^\]*\].*feed gen-server crashed`)
var re_feedstale, _ = regexp.Compile(` FEED\[<=>[^\]*\].*feed.*stale`)
var re_feedfatal, _ = regexp.Compile(`[Fatal].*FEED\[<=>[^\]*\].*feed.*stale`)
var re_feedwarn, _ = regexp.Compile(`[Warn].*FEED\[<=>[^\]*\].*feed.*stale`)
var re_feederror, _ = regexp.Compile(`[Error].*FEED\[<=>[^\]*\].*feed.*stale`)
var re_feedengns, _ = regexp.Compile(`[Error].*FEED\[<=>[^\]*\].*engine`)
var fmt_feedendps = `[Error].*ENDP\[<-[^#]*#%s]`

// log messages

var regs_timestamp = `^(\d\d:\d\d:\d\d.\d\d\d\d\d\d) `
var regs_level = `\[(Fatal|Error|Warn|Debug|Info|Trace)\] `
var regs_comp = `(PROJ|PRAM|FEED|KVDT|VBRT|ENDP)`
var re_goport, _ = regexp.Compile(`^\[goport\]`)
var re_basic, _ = regexp.Compile(regs_timestamp + regs_level + regs_comp)

type LogMsg struct {
	msg   string
	ts    string
	level string
	kind  string
}

func newLogMsg(lines []string) (msg *LogMsg, skipped, remlines []string) {
	if len(lines) == 0 {
		return nil, nil, nil
	}
	skipped = make([]string, 0)
	i := 0
	msg = &LogMsg{msg: "", kind: ""}
	for ; i < len(lines); i++ {
		line := lines[i]
		if msg.msg != "" && strings.HasPrefix(line, "    ") {
			msg.msg = msg.msg + "\n" + line
		} else if msg.msg != "" {
			return msg, skipped, lines[i:]
		} else {
			msg.msg = line
			ok := msg.basic()
			if !ok {
				msg.msg = ""
				line = strings.Trim(line, "\t \r\n")
				if line != "" {
					skipped = append(skipped, line)
				}
			}
		}
	}
	return msg, skipped, nil
}

func (msg *LogMsg) basic() bool {
	bs := []byte(msg.msg)
	if re_goport.Match(bs) {
		msg.kind = "goport"
	} else if m := re_basic.FindStringSubmatch(msg.msg); m != nil {
		msg.ts, msg.level, msg.kind = m[1], m[2], m[3]
	} else {
		return false
	}
	return true
}

func (msg *LogMsg) isGoport() bool {
	return msg.kind == "goport"
}

func (msg *LogMsg) isEmpty() bool {
	return msg.msg == ""
}

// operations on list of messages

type LogMsgs []*LogMsg

func (msgs LogMsgs) goports() []LogMsgs {
	if len(msgs) == 0 {
		return nil
	}
	blocks := make([]LogMsgs, 0)
	block := make(LogMsgs, 0)
	for _, msg := range msgs {
		if re_goport.Match([]byte(msg.msg)) {
			blocks = append(blocks, block)
			block = make(LogMsgs, 0)
		}
		block = append(block, msg)
	}
	if len(block) > 0 {
		blocks = append(blocks, block)
	}
	return blocks
}
<commit_msg>tool for analysing projector log.<commit_after>package main

import "flag"
import "os"
import "log"
import "fmt"
import "strings"
import "regexp"
import "io/ioutil"

var options struct {
	show    []string
	session int
}

func argParse() []string {
	var show string

	flag.StringVar(&show, "show", "", "log lines to show")
	flag.IntVar(&options.session, "session", 0, "session to analyse")

	flag.Parse()

	for _, s := range strings.Split(show, ",") {
		if options.show == nil {
			options.show = []string{}
		}
		options.show = append(options.show, strings.ToLower(s))
	}
	args := flag.Args()
	if len(args) == 0 {
		log.Fatalf("Please specify a log file!!")
	}

	// validate whether log files are present
	for _, logfile := range args {
		if _, err := os.Stat(logfile); err != nil {
			log.Fatal(err)
		}
	}
	return args
}

func main() {
	args := argParse()
	analyseLog(args[0])
}

func analyseLog(logfile string) {
	var msg *LogMsg
	var skips []string

	// convert lines to log-messages
	lines := readLines(logfile)
	msgs := make([]*LogMsg, 0)
	skipped := make([]string, 0)
	for len(lines) > 0 {
		msg, skips, lines = lines2msg(lines)
		if !msg.isEmpty() {
			msgs = append(msgs, msg)
		}
		skipped = append(skipped, skips...)
	}
	validate(msgs)

	// log messages to goport-sessions.
	sessions := getSessions(msgs)

	fmt.Printf("Number of messages: %d\n", len(msgs))
	fmt.Printf("Number of goport-sessions: %d\n", len(sessions))
	fmt.Printf("Lines skipped: %d\n", len(skipped))

	session := sessions[options.session]
	analyseSession(session)

	for _, show := range options.show {
		switch show {
		case "skip":
			listLines(skipped)
		}
	}
}

func analyseSession(session LogMsgs) {
	// gather requests
	requests := gatherRequests(session)
	for _, show := range options.show {
		var reqs Requests
		switch show {
		case "vbmaprequest":
			reqs = requests.vbmapRequests()
		case "failoverlog":
			reqs = requests.failoverLogRequests()
		case "mutationtopic":
			reqs = requests.mutationTopicRequests()
		case "restartvbuckets":
			reqs = requests.restartVbucketsRequests()
		case "shutdownvbuckets":
			reqs = requests.shutdownVbucketsRequests()
		case "addbuckets":
			reqs = requests.addBucketsRequests()
		case "delbuckets":
			reqs = requests.delBucketsRequests()
		case "addinstances":
			reqs = requests.addInstancesRequests()
		case "delinstances":
			reqs = requests.delInstancesRequests()
		case "repairendpoints":
			reqs = requests.repairEndpointsRequests()
		case "shutdowntopic":
			reqs = requests.shutdownTopics()
		}
		fmt.Printf("Total of %v requests for %v\n", len(reqs), show)
		for _, r := range reqs {
			r.printReq()
		}
	}
}

// parse feeds

type FeedLines struct {
	name      string
	crashed   []string
	stales    []string
	fatals    []string
	errors    []string
	warns     []string
	engines   []string
	endpoints map[string][]string // endpid -> lines
	byopaque  map[string][]string // opaque -> lines
}

var re_feed, _ = regexp.Compile(` FEED\[<=>([^\(]*)(`)
var re_feedcrashed, _ = regexp.Compile(` FEED\[<=>[^\]*\].*feed gen-server crashed`)
var re_feedstale, _ = regexp.Compile(` FEED\[<=>[^\]*\].*feed.*stale`)
var re_feedfatal, _ = regexp.Compile(`[Fatal].*FEED\[<=>[^\]*\].*feed.*stale`)
var re_feedwarn, _ = regexp.Compile(`[Warn].*FEED\[<=>[^\]*\].*feed.*stale`)
var re_feederror, _ = regexp.Compile(`[Error].*FEED\[<=>[^\]*\].*feed.*stale`)
var re_feedengns, _ = regexp.Compile(`[Error].*FEED\[<=>[^\]*\].*engine`)
var fmt_feedendps = `[Error].*ENDP\[<-[^#]*#%s]`

//-------------
// log messages
//-------------

var re_goport, _ = regexp.Compile(
	`^\[goport\] (\d\d\d\d/\d\d/\d\d \d\d:\d\d:\d\d) `)
var re_basic, _ = regexp.Compile(
	`^(\d\d:\d\d:\d\d.\d\d\d\d\d\d) ` +
		`\[(Fatal|Error|Warn|Debug|Info|Trace)\] ` +
		`(PROJ|PRAM|FEED|KVDT|VBRT|ENDP|DCPT).*( ##[0-9a-f]+ )?`)
var re_settings, _ = regexp.Compile(
	`^(\d\d:\d\d:\d\d.\d\d\d\d\d\d) \[(Info)\] New settings`)
var re_angio, _ = regexp.Compile(` (##[0-9a-f]+) `)
var re_reqType, _ = regexp.Compile(
	`(doVbmapRequest|` +
		`doFailoverLog|` +
		`doMutationTopic|` +
		`doRestartVbuckets|` +
		`doShutdownVbuckets|` +
		`doAddBuckets|` +
		`doDelBuckets|` +
		`doAddInstances|` +
		`doDelInstances|` +
		`doRepairEndpoints|` +
		`doShutdownTopic)\(\)`)

type LogMsg struct {
	msg      string
	ts       string
	level    string
	kind     string
	reqType  string
	feedName string
	angio    string
}

// convert one or more log lines into a log messages.
func lines2msg(lines []string) (msg *LogMsg, skipped, remlines []string) {
	if len(lines) == 0 {
		return nil, nil, nil
	}
	skipped = make([]string, 0)
	msg = &LogMsg{msg: "", kind: ""}
	ok, n := false, -1
	for i, line := range lines {
		if (msg.msg != "") && ((n > 0) || strings.HasPrefix(line, "    ")) {
			msg.msg = msg.msg + "\n" + line // amend with previous line
			n--

		} else if msg.msg != "" {
			return msg, skipped, lines[i:]

		} else {
			msg.msg = line
			if ok, n = msg.parseHeadLine(); !ok {
				msg.msg = ""
				if strings.Trim(line, "\t \r\n") != "" {
					skipped = append(skipped, line)
				}
			}
		}
	}
	return msg, skipped, nil
}

func (msg *LogMsg) parseHeadLine() (bool, int) {
	if m := re_goport.FindStringSubmatch(msg.msg); m != nil {
		msg.ts, msg.kind = m[1], "goport"
	} else if m := re_basic.FindStringSubmatch(msg.msg); m != nil {
		msg.ts, msg.level, msg.kind = m[1], m[2], m[3]
		if m = re_angio.FindStringSubmatch(msg.msg); m != nil {
			msg.angio = m[1]
		}
		if m = re_reqType.FindStringSubmatch(msg.msg); m != nil {
			msg.reqType = m[1]
		}
	} else if m := re_settings.FindStringSubmatch(msg.msg); m != nil {
		msg.ts, msg.level, msg.kind = m[1], m[2], "settings"
		return true, 1
	} else {
		return false, 0
	}
	return true, -1
}

func (msg *LogMsg) isGoport() bool {
	return msg.kind == "goport"
}

func (msg *LogMsg) isEmpty() bool {
	return msg.msg == ""
}

func (msg *LogMsg) hasPattern(arg interface{}) bool {
	var re *regexp.Regexp
	var err error
	if s, ok := arg.(string); ok {
		if re, err = regexp.Compile(s); err != nil {
			log.Fatal(err)
		}
	} else if re, ok = arg.(*regexp.Regexp); !ok {
		log.Fatalf("unexpected arg %T %v\n", arg, arg)
	}
	return re.Match([]byte(msg.msg))
}

func (msg *LogMsg) hasAngio(angio string) bool {
	return (angio != "") && (msg.angio == angio)
}

func (msg *LogMsg) isVbmapRequest() bool {
	return msg.reqType == "doVbmapRequest"
}

func (msg *LogMsg) isFailoverLog() bool {
	return msg.reqType == "doFailoverLog"
}

func (msg *LogMsg) isMutationTopic() bool {
	return msg.reqType == "doMutationTopic"
}

func (msg *LogMsg) isRestartVbuckets() bool {
	return msg.reqType == "doRestartVbuckets"
}

func (msg *LogMsg) isShutdownVbuckets() bool {
	return msg.reqType == "doShutdownVbuckets"
}

func (msg *LogMsg) isAddBuckets() bool {
	return msg.reqType == "doAddBuckets"
}

func (msg *LogMsg) isDelBuckets() bool {
	return msg.reqType == "doDelBuckets"
}

func (msg *LogMsg) isAddInstances() bool {
	return msg.reqType == "doAddInstances"
}

func (msg *LogMsg) isDelInstances() bool {
	return msg.reqType == "doDelInstances"
}

func (msg *LogMsg) isRepairEndpoints() bool {
	return msg.reqType == "doRepairEndpoints"
}

func (msg *LogMsg) isShutdownTopic() bool {
	return msg.reqType == "doShutdownTopic"
}

//---------
// sessions
//---------

type LogMsgs []*LogMsg

func getSessions(msgs []*LogMsg) []LogMsgs {
	if len(msgs) == 0 {
		return nil
	}
	sessions := make([]LogMsgs, 0)
	session := make(LogMsgs, 0)
	for _, msg := range msgs {
		if msg.kind == "goport" && len(session) > 0 {
			sessions = append(sessions, session)
			session = make(LogMsgs, 0)
		}
		session = append(session, msg)
	}
	if len(session) > 0 {
		sessions = append(sessions, session)
	}
	return sessions
}

//---------------
// track requests
//---------------

func gatherRequests(msgs LogMsgs) Requests {
	cache := map[string]bool{}
	requests := []*Request{}
	for len(msgs) > 0 {
		msg := msgs[0]
		if _, ok := cache[msg.angio]; msg.angio != "" && !ok {
			r := newRequest(msg)
			msgs = r.trackRequest(msg.angio, msgs)
			cache[msg.angio] = true
			requests = append(requests, r)
		} else {
			msgs = msgs[1:]
		}
	}
	return requests
}

//--------
// request
//--------

type Request struct {
	reqmsg *LogMsg
	typ    string
	msgs   []*LogMsg
}

func newRequest(reqmsg *LogMsg) *Request {
	return &Request{
		reqmsg: reqmsg,
		msgs:   make([]*LogMsg, 0),
	}
}

func (r *Request) trackRequest(angio string, msgs []*LogMsg) (rems []*LogMsg) {
	rems = make([]*LogMsg, 0)
	for _, msg := range msgs {
		if msg.hasAngio(angio) {
			r.msgs = append(r.msgs, msg)
		} else {
			rems = append(rems, msg)
		}
	}
	return rems
}

func (r *Request) printReq() {
	for _, msg := range r.msgs {
		fmt.Println(msg.msg)
	}
}

type Requests []*Request

func (rs Requests) vbmapRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isVbmapRequest() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) failoverLogRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isFailoverLog() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) mutationTopicRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isMutationTopic() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) restartVbucketsRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isRestartVbuckets() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) shutdownVbucketsRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isShutdownVbuckets() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) addBucketsRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isAddBuckets() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) delBucketsRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isDelBuckets() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) addInstancesRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isAddInstances() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) delInstancesRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isDelInstances() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) repairEndpointsRequests() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isRepairEndpoints() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

func (rs Requests) shutdownTopics() Requests {
	reqs := make(Requests, 0)
	for _, r := range rs {
		if r.reqmsg.isShutdownTopic() {
			reqs = append(reqs, r)
		}
	}
	return reqs
}

//------------------------
// Validation on log lines.
//------------------------

func validate(msgs []*LogMsg) {
	if !validatePRAMRegistration(msgs) {
		log.Fatalf("validatePRAMRegistration\n")
	}
}

func validatePRAMRegistration(msgs []*LogMsg) bool {
	re, err := regexp.Compile(
		`registered.*` +
			`(/adminport/vbmapRequest|` +
			`/adminport/failoverLogRequest|` +
			`/adminport/mutationTopicRequest|` +
			`/adminport/restartVbucketsRequest|` +
			`/adminport/shutdownVbucketsRequest|` +
			`/adminport/addBucketsRequest|` +
			`/adminport/delBucketsRequest|` +
			`/adminport/addInstancesRequest|` +
			`/adminport/delInstancesRequest|` +
			`/adminport/repairEndpointsRequest|` +
			`/adminport/shutdownTopicRequest|` +
			`/adminport/stats|` +
			`/stats|` +
			`/settings)`)
	if err != nil {
		log.Fatal(err)
	}
	count := 0
	for _, msg := range msgs {
		if msg.hasPattern(re) {
			count++
		}
	}
	if count != 14 {
		log.Printf("validaterPRAMRegistration(): %v", count)
		return false
	}
	return true
}

//----------------
// local functions
//----------------

func readLines(file string) []string {
	s, err := ioutil.ReadFile(file)
	if err != nil {
		log.Fatal(err)
	}
	return strings.Split(string(s), "\n")
}

func listLines(lines []string) {
	for _, line := range lines {
		fmt.Println(line)
	}
}
<|endoftext|>


<commit_before>package jsonselect

import (
    "io/ioutil"
    "reflect"
    "strings"
    "testing"
    "github.com/coddingtonbear/go-simplejson"
)

// Used for storing the results of the benchmarking tests below
// to avoid compiler optimizations.
var parser *Parser
var values []interface{}

func getTestParser(testDocuments map[string]*simplejson.Json, testName string) (*Parser, error) {
    jsonDocument := testDocuments[testName[0:strings.Index(testName, "_")]]
    return CreateParser(jsonDocument)
}

func runTestsInDirectory(t *testing.T, baseDirectory string) {
    var testDocuments = make(map[string]*simplejson.Json)
    var testSelectors = make(map[string]string)
    var testOutput = make(map[string][]string)

    files, err := ioutil.ReadDir(baseDirectory)
    if err != nil {
        t.Error("Error encountered while loading conformance tests ", err)
    }

    for _, fileInfo := range(files) {
        name := fileInfo.Name()
        if strings.HasSuffix(name, ".json") {
            json_document, err := ioutil.ReadFile(baseDirectory + name)
            if err != nil {
                t.Error("Error encountered while reading ", name, ": ", err)
                continue
            }
            parsed_document, err := simplejson.NewJson(json_document)
            if err != nil {
                t.Error("Error encountered while deserializing ", name, ": ", err)
                continue
            }
            testDocuments[name[0:len(name)-len(".json")]] = parsed_document
        } else if (strings.HasSuffix(name, ".output")) {
            output_document, err := ioutil.ReadFile(baseDirectory + name)
            if err != nil {
                t.Error("Error encountered while reading ", name, ": ", err)
                continue
            }
            if strings.HasPrefix(string(output_document), "Error") {
                // We won't be handling errors in the same way.
                continue
            }
            var actualOutput []string
            var stringTemporary string
            for _, str := range strings.Split(string(output_document), "\n") {
                stringTemporary = stringTemporary + str
                // Try to parse -- if it works, we have the whole object
                if strings.Index(stringTemporary, "{") == 0 {
                    if strings.Count(stringTemporary, "{") != strings.Count(stringTemporary, "}") {
                        continue
                    }
                    actualOutput = append(actualOutput, stringTemporary)
                    stringTemporary = ""
                } else if strings.Index(stringTemporary, "[") == 0 {
                    if strings.Count(stringTemporary, "[") != strings.Count(stringTemporary, "]") {
                        continue
                    }
                    actualOutput = append(actualOutput, stringTemporary)
                    stringTemporary = ""
                } else if len(stringTemporary) > 0 {
                    actualOutput = append(actualOutput, stringTemporary)
                    stringTemporary = ""
                }
            }
            testOutput[name[0:len(name)-len(".output")]] = actualOutput
        } else if (strings.HasSuffix(name, ".selector")) {
            selector_document, err := ioutil.ReadFile(baseDirectory + name)
            if err != nil {
                t.Error("Error encountered while reading ", name, ": ", err)
                continue
            }
            testSelectors[name[0:len(name)-len(".selector")]] = string(selector_document)
        }
    }

    for testName := range testOutput {
        var passed bool = true
        t.Log("Running test ", testName)
        parser, err := getTestParser(testDocuments, testName)
        if err != nil {
            t.Error("Test ", testName, "failed: ", err)
            passed = false
        }
        selectorString := testSelectors[testName]
        expectedOutput := testOutput[testName]

        results, err := parser.GetJsonElements(selectorString)
        if err != nil {
            t.Error("Test ", testName, "failed: ", err)
            passed = false
        }
        var stringResults []string
        for _, result := range results {
            encoded, err := result.Encode()
            if err != nil {
                t.Error("Test ", testName, "failed: ", err)
                passed = false
            }
            stringResults = append(stringResults, string(encoded))
        }

        if len(stringResults) != len(expectedOutput) {
            t.Error("Test ", testName, " failed due to number of results being mismatched; ", len(stringResults), " != ", len(expectedOutput), ": [Actual] ", stringResults, " != [Expected] ", expectedOutput)
            passed = false
        } else {
            var matched bool = true
            for idx, result := range stringResults {
                expectedEncoded := expectedOutput[idx]
                // If the string begins with {, let's load it using simplejson,
                // convert it to a map, and use reflection to see if they do match.
                if strings.Index(strings.TrimSpace(expectedEncoded), "{") == 0 {
                    expectedJson, err := simplejson.NewJson([]byte(expectedEncoded))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding expectation: ", err,
                        )
                        passed = false
                    }
                    resultJson, err := simplejson.NewJson([]byte(result))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding result: ", err,
                        )
                        passed = false
                    }
                    result := reflect.DeepEqual(
                        expectedJson.MustMap(),
                        resultJson.MustMap(),
                    )
                    if !result {
                        matched = false
                    }
                } else if strings.Index(strings.TrimSpace(expectedEncoded), "[") == 0 {
                    expectedJson, err := simplejson.NewJson([]byte(expectedEncoded))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding expectation: ", err,
                        )
                        passed = false
                    }
                    resultJson, err := simplejson.NewJson([]byte(result))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding result: ", err,
                        )
                        passed = false
                    }
                    result := reflect.DeepEqual(
                        expectedJson.MustArray(),
                        resultJson.MustArray(),
                    )
                    if !result {
                        matched = false
                    }
                } else if expectedEncoded != result {
                    matched = false
                }
                if !matched {
                    t.Error(
                        "Test ", testName, " failed on item #", idx, ": [Actual] ", result, " != [Expected] ", expectedEncoded,
                    )
                    passed = false
                }
            }
        }
        if passed {
            t.Log("Test ", testName, " PASSED")
        }
    }
}

func TestLevel1(t *testing.T) {
    runTestsInDirectory(t, "./conformance_tests/level_1/")
}

func TestLevel2(t *testing.T) {
    runTestsInDirectory(t, "./conformance_tests/level_2/")
}

func TestLevel3(t *testing.T) {
    runTestsInDirectory(t, "./conformance_tests/level_3/")
}

func BenchmarkParseDocument(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        parser, _ = CreateParserFromString(string(json_ast))
    }
}

func BenchmarkBasicSelector(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`.Link`)
    }
}

func BenchmarkComplexSelector(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`.Link object:has(.Str:val("News"))`)
    }
}

func BenchmarkAncestorSelector(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`.Link object`)
    }
}

func BenchmarkHasExpression(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`object:has(.Str:val("News"))`)
    }
}
<commit_msg>Rewriting jsonselect test for Go 1.3 support.<commit_after>package jsonselect

import (
    "io/ioutil"
    "reflect"
    "strings"
    "testing"
    "github.com/coddingtonbear/go-simplejson"
)

// Used for storing the results of the benchmarking tests below
// to avoid compiler optimizations.
var parser *Parser
var values []interface{}

func getTestParser(testDocuments map[string]*simplejson.Json, testName string) (*Parser, error) {
    jsonDocument := testDocuments[testName[0:strings.Index(testName, "_")]]
    return CreateParser(jsonDocument)
}

func runTestsInDirectory(t *testing.T, baseDirectory string) {
    var testDocuments = make(map[string]*simplejson.Json)
    var testSelectors = make(map[string]string)
    var testOutput = make(map[string][]string)

    files, err := ioutil.ReadDir(baseDirectory)
    if err != nil {
        t.Error("Error encountered while loading conformance tests ", err)
    }

    for _, fileInfo := range(files) {
        name := fileInfo.Name()
        if strings.HasSuffix(name, ".json") {
            json_document, err := ioutil.ReadFile(baseDirectory + name)
            if err != nil {
                t.Error("Error encountered while reading ", name, ": ", err)
                continue
            }
            parsed_document, err := simplejson.NewJson(json_document)
            if err != nil {
                t.Error("Error encountered while deserializing ", name, ": ", err)
                continue
            }
            testDocuments[name[0:len(name)-len(".json")]] = parsed_document
        } else if (strings.HasSuffix(name, ".output")) {
            output_document, err := ioutil.ReadFile(baseDirectory + name)
            if err != nil {
                t.Error("Error encountered while reading ", name, ": ", err)
                continue
            }
            if strings.HasPrefix(string(output_document), "Error") {
                // We won't be handling errors in the same way.
                continue
            }
            var actualOutput []string
            var stringTemporary string
            for _, str := range strings.Split(string(output_document), "\n") {
                stringTemporary = stringTemporary + str
                // Try to parse -- if it works, we have the whole object
                if strings.Index(stringTemporary, "{") == 0 {
                    if strings.Count(stringTemporary, "{") != strings.Count(stringTemporary, "}") {
                        continue
                    }
                    actualOutput = append(actualOutput, stringTemporary)
                    stringTemporary = ""
                } else if strings.Index(stringTemporary, "[") == 0 {
                    if strings.Count(stringTemporary, "[") != strings.Count(stringTemporary, "]") {
                        continue
                    }
                    actualOutput = append(actualOutput, stringTemporary)
                    stringTemporary = ""
                } else if len(stringTemporary) > 0 {
                    actualOutput = append(actualOutput, stringTemporary)
                    stringTemporary = ""
                }
            }
            testOutput[name[0:len(name)-len(".output")]] = actualOutput
        } else if (strings.HasSuffix(name, ".selector")) {
            selector_document, err := ioutil.ReadFile(baseDirectory + name)
            if err != nil {
                t.Error("Error encountered while reading ", name, ": ", err)
                continue
            }
            testSelectors[name[0:len(name)-len(".selector")]] = string(selector_document)
        }
    }

    for testName := range testOutput {
        var passed bool = true
        t.Log("Running test ", testName)
        parser, err := getTestParser(testDocuments, testName)
        if err != nil {
            t.Error("Test ", testName, "failed: ", err)
            passed = false
        }
        selectorString := testSelectors[testName]
        expectedOutput := testOutput[testName]

        results, err := parser.GetJsonElements(selectorString)
        if err != nil {
            t.Error("Test ", testName, "failed: ", err)
            passed = false
        }
        var stringResults []string
        for _, result := range results {
            encoded, err := result.Encode()
            if err != nil {
                t.Error("Test ", testName, "failed: ", err)
                passed = false
            }
            stringResults = append(stringResults, string(encoded))
        }

        if len(stringResults) != len(expectedOutput) {
            t.Error("Test ", testName, " failed due to number of results being mismatched; ", len(stringResults), " != ", len(expectedOutput), ": [Actual] ", stringResults, " != [Expected] ", expectedOutput)
            passed = false
        } else {
            var expected = make([]*simplejson.Json, 0, 10)
            var actual = make([]*simplejson.Json, 0, 10)
            matchType := "string"

            for idx, result := range stringResults {
                expectedEncoded := expectedOutput[idx]
                // If the string begins with {, let's load it using simplejson,
                // convert it to a map, and use reflection to see if they do match.
                if strings.Index(strings.TrimSpace(expectedEncoded), "{") == 0 {
                    expectedJson, err := simplejson.NewJson([]byte(expectedEncoded))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding expectation: ", err,
                        )
                        passed = false
                    }
                    resultJson, err := simplejson.NewJson([]byte(result))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding result: ", err,
                        )
                        passed = false
                    }

                    expected = append(expected, expectedJson)
                    actual = append(actual, resultJson)
                    matchType = "map"
                } else if strings.Index(strings.TrimSpace(expectedEncoded), "[") == 0 {
                    expectedJson, err := simplejson.NewJson([]byte(expectedEncoded))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding expectation: ", err,
                        )
                        passed = false
                    }
                    resultJson, err := simplejson.NewJson([]byte(result))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding result: ", err,
                        )
                        passed = false
                    }

                    expected = append(expected, expectedJson)
                    actual = append(actual, resultJson)
                    matchType = "array"
                } else if expectedEncoded != result {
                    expectedJson, err := simplejson.NewJson([]byte(expectedEncoded))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding expectation: ", err,
                        )
                        passed = false
                    }
                    resultJson, err := simplejson.NewJson([]byte(result))
                    if err != nil {
                        t.Error(
                            "Test ", testName, " failed due to a JSON decoding error while decoding result: ", err,
                        )
                        passed = false
                    }

                    expected = append(expected, expectedJson)
                    actual = append(actual, resultJson)
                    matchType = "string"
                }
            }

            // Iterate over each of the actual elements; if:
            // * We find a match, remove the match from the expected.
            // * We do not find a match, report an error
            for _, actualElement := range actual {
                var matched bool = false
                for expectedIdx, expectedElement := range expected {
                    if matchType == "map" {
                        matched = reflect.DeepEqual(
                            expectedElement.MustMap(),
                            actualElement.MustMap(),
                        )
                    } else if matchType == "array" {
                        matched = reflect.DeepEqual(
                            expectedElement.MustArray(),
                            actualElement.MustArray(),
                        )
                    } else if matchType == "string" {
                        matched = reflect.DeepEqual(
                            expectedElement.MustString(),
                            actualElement.MustString(),
                        )
                    }
                    if matched {
                        expected = append(
                            expected[:expectedIdx],
                            expected[expectedIdx+1:]...
                        )
                        break
                    }
                }
                if !matched {
                    t.Error("Element ", actual, " not found in ", expected)
                    passed = false
                    break;
                }
            }
            if len(expected) > 0 {
                t.Error("Elements missing from output: ", expected)
                passed = false
            }
        }
        if passed {
            t.Log("Test ", testName, " PASSED")
        } else {
            t.Error("Test ", testName, " FAILED")
        }
    }
}

func TestLevel1(t *testing.T) {
    runTestsInDirectory(t, "./conformance_tests/level_1/")
}

func TestLevel2(t *testing.T) {
    runTestsInDirectory(t, "./conformance_tests/level_2/")
}

func TestLevel3(t *testing.T) {
    runTestsInDirectory(t, "./conformance_tests/level_3/")
}

func BenchmarkParseDocument(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        parser, _ = CreateParserFromString(string(json_ast))
    }
}

func BenchmarkBasicSelector(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`.Link`)
    }
}

func BenchmarkComplexSelector(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`.Link object:has(.Str:val("News"))`)
    }
}

func BenchmarkAncestorSelector(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`.Link object`)
    }
}

func BenchmarkHasExpression(b *testing.B) {
    json_ast, _ := ioutil.ReadFile("./test_data/example_json_ast.json")
    parser, _ = CreateParserFromString(string(json_ast))
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        values, _ = parser.GetValues(`object:has(.Str:val("News"))`)
    }
}
<|endoftext|>


<commit_before>package steps_test

import (
	"errors"
	"sync"

	"github.com/hashicorp/go-multierror"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/cloudfoundry-incubator/executor/depot/steps"
	"github.com/cloudfoundry-incubator/executor/depot/steps/fakes"
)

var _ = Describe("CodependentStep", func() {
	var step steps.Step
	var subStep1 *fakes.FakeStep
	var subStep2 *fakes.FakeStep

	var thingHappened chan bool
	var cancelled chan bool

	var errorOnExit bool

	BeforeEach(func() {
		errorOnExit = false

		thingHappened = make(chan bool, 2)
		cancelled = make(chan bool, 2)

		running := new(sync.WaitGroup)
		running.Add(2)

		subStep1 = &fakes.FakeStep{
			PerformStub: func() error {
				running.Done()
				running.Wait()
				thingHappened <- true
				return nil
			},
			CancelStub: func() {
				cancelled <- true
			},
		}

		subStep2 = &fakes.FakeStep{
			PerformStub: func() error {
				running.Done()
				running.Wait()
				thingHappened <- true
				return nil
			},
			CancelStub: func() {
				cancelled <- true
			},
		}
	})

	JustBeforeEach(func() {
		step = steps.NewCodependent([]steps.Step{subStep1, subStep2}, errorOnExit)
	})

	Describe("Perform", func() {
		It("performs its substeps in parallel", func() {
			err := step.Perform()
			Expect(err).NotTo(HaveOccurred())

			Eventually(thingHappened).Should(Receive())
			Eventually(thingHappened).Should(Receive())
		})

		Context("when one of the substeps fails", func() {
			disaster := errors.New("oh no!")

			BeforeEach(func() {
				subStep1 = &fakes.FakeStep{
					PerformStub: func() error {
						return disaster
					},
				}

				subStep2 = &fakes.FakeStep{
					PerformStub: func() error {
						return nil
					},
				}
			})

			It("returns an aggregate of the failures", func() {
				err := step.Perform()
				Expect(err.(*multierror.Error).WrappedErrors()).To(ConsistOf(disaster))
			})

			It("cancels all the steps", func() {
				step.Perform()

				Expect(subStep1.CancelCallCount()).To(Equal(1))
				Expect(subStep2.CancelCallCount()).To(Equal(1))
			})
		})

		Context("when one of the substeps exits without failure", func() {
			var (
				cancelledError, err error
				errCh               chan error
			)

			BeforeEach(func() {
				cancelledError = errors.New("I was cancelled yo.")
				cancelled2 := make(chan bool)

				subStep1.PerformStub = func() error {
					return nil
				}

				subStep2.PerformStub = func() error {
					<-cancelled2
					return cancelledError
				}

				subStep2.CancelStub = func() {
					cancelled2 <- true
				}
			})

			JustBeforeEach(func() {
				errCh = make(chan error)

				go func() {
					errCh <- step.Perform()
				}()
			})

			It("continues to performe the other step", func() {
				Eventually(errCh).ShouldNot(Receive())
			})

			Context("when errorOnExit is set to true", func() {
				BeforeEach(func() {
					errorOnExit = true
				})

				It("returns an aggregate of the failures", func() {
					Eventually(errCh).Should(Receive(&err))
					Expect(err.(*multierror.Error).WrappedErrors()).To(ConsistOf(cancelledError, steps.CodependentStepExitedError))
				})

				It("cancels all the steps", func() {
					Eventually(errCh).Should(Receive())

					Expect(subStep1.CancelCallCount()).To(Equal(1))
					Expect(subStep2.CancelCallCount()).To(Equal(1))
				})
			})
		})
	})

	Describe("Cancel", func() {
		It("cancels all sub-steps", func() {
			step1 := &fakes.FakeStep{}
			step2 := &fakes.FakeStep{}
			step3 := &fakes.FakeStep{}

			sequence := steps.NewCodependent([]steps.Step{step1, step2, step3}, errorOnExit)

			sequence.Cancel()

			Expect(step1.CancelCallCount()).To(Equal(1))
			Expect(step2.CancelCallCount()).To(Equal(1))
			Expect(step3.CancelCallCount()).To(Equal(1))
		})
	})
})
<commit_msg>Wait until all steps are finished<commit_after>package steps_test

import (
	"errors"
	"sync"

	"github.com/hashicorp/go-multierror"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/cloudfoundry-incubator/executor/depot/steps"
	"github.com/cloudfoundry-incubator/executor/depot/steps/fakes"
)

var _ = Describe("CodependentStep", func() {
	var step steps.Step
	var subStep1 *fakes.FakeStep
	var subStep2 *fakes.FakeStep

	var thingHappened chan bool
	var cancelled chan bool

	var errorOnExit bool

	BeforeEach(func() {
		errorOnExit = false

		thingHappened = make(chan bool, 2)
		cancelled = make(chan bool, 2)

		running := new(sync.WaitGroup)
		running.Add(2)

		subStep1 = &fakes.FakeStep{
			PerformStub: func() error {
				running.Done()
				running.Wait()
				thingHappened <- true
				return nil
			},
			CancelStub: func() {
				cancelled <- true
			},
		}

		subStep2 = &fakes.FakeStep{
			PerformStub: func() error {
				running.Done()
				running.Wait()
				thingHappened <- true
				return nil
			},
			CancelStub: func() {
				cancelled <- true
			},
		}
	})

	Describe("Perform", func() {
		JustBeforeEach(func() {
			step = steps.NewCodependent([]steps.Step{subStep1, subStep2}, errorOnExit)
		})

		It("performs its substeps in parallel", func() {
			err := step.Perform()
			Expect(err).NotTo(HaveOccurred())

			Eventually(thingHappened).Should(Receive())
			Eventually(thingHappened).Should(Receive())
		})

		Context("when one of the substeps fails", func() {
			disaster := errors.New("oh no!")

			BeforeEach(func() {
				subStep1 = &fakes.FakeStep{
					PerformStub: func() error {
						return disaster
					},
				}

				subStep2 = &fakes.FakeStep{
					PerformStub: func() error {
						return nil
					},
				}
			})

			It("returns an aggregate of the failures", func() {
				err := step.Perform()
				Expect(err.(*multierror.Error).WrappedErrors()).To(ConsistOf(disaster))
			})

			It("cancels all the steps", func() {
				step.Perform()

				Expect(subStep1.CancelCallCount()).To(Equal(1))
				Expect(subStep2.CancelCallCount()).To(Equal(1))
			})
		})

		Context("when one of the substeps exits without failure", func() {
			var (
				cancelledError, err error
				errCh               chan error
			)

			BeforeEach(func() {
				cancelledError = errors.New("I was cancelled yo.")
				cancelled2 := make(chan bool, 1)

				subStep1.PerformStub = func() error {
					return nil
				}

				subStep2.PerformStub = func() error {
					<-cancelled2
					return cancelledError
				}

				subStep2.CancelStub = func() {
					cancelled2 <- true
				}
			})

			JustBeforeEach(func() {
				errCh = make(chan error)

				go func() {
					errCh <- step.Perform()
				}()
			})

			It("continues to perform the other step", func() {
				Consistently(errCh).ShouldNot(Receive())

				By("cancelling, it should return")
				step.Cancel()
				Eventually(errCh).Should(Receive())
			})

			Context("when errorOnExit is set to true", func() {
				BeforeEach(func() {
					errorOnExit = true
				})

				It("returns an aggregate of the failures", func() {
					Eventually(errCh).Should(Receive(&err))
					Expect(err.(*multierror.Error).WrappedErrors()).To(ConsistOf(cancelledError, steps.CodependentStepExitedError))
				})

				It("cancels all the steps", func() {
					Eventually(errCh).Should(Receive())

					Expect(subStep1.CancelCallCount()).To(Equal(1))
					Expect(subStep2.CancelCallCount()).To(Equal(1))
				})
			})
		})
	})

	Describe("Cancel", func() {
		It("cancels all sub-steps", func() {
			step1 := &fakes.FakeStep{}
			step2 := &fakes.FakeStep{}
			step3 := &fakes.FakeStep{}

			sequence := steps.NewCodependent([]steps.Step{step1, step2, step3}, errorOnExit)

			sequence.Cancel()

			Expect(step1.CancelCallCount()).To(Equal(1))
			Expect(step2.CancelCallCount()).To(Equal(1))
			Expect(step3.CancelCallCount()).To(Equal(1))
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

type dummyAuth struct {
	usernameSent, passwordSent string
	called                     int
	ret                        bool
}

func (a *dummyAuth) authenticate(username, password string) (bool, error) {
	a.usernameSent = username
	a.passwordSent = password
	a.called += 1
	return a.ret, nil
}

func TestRootHandlerServesLoaderScript(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/", nil)

	server := server{config: configuration{LoaderScript: "testdata/loader.sh"}}
	server.rootHandler(response, request)

	code := response.Code
	if code != 200 {
		t.Error("response code was not 200:", code)
	}

	body := response.Body.String()
	if body != "awesome loader script" {
		t.Error("wrong response from / endpoint:", body)
	}
}

func TestKeysHandlerRequiresAuthentication(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	server := server{authenticator: &dummyAuth{}, config: configuration{SSHKey: "testdata/id_rsa"}}
	server.keyHandler(response, request)

	code := response.Code
	if code != 401 {
		t.Error("request should have been rejected with 401:", code)
	}

	header := response.Header().Get("Authenticate")
	if header != "KeyGuard" {
		t.Error("correct authenticate header was not in response:", header)
	}
}

func TestKeysHandlerRequiresValidCredentials(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	request.SetBasicAuth("cromega", "supersecurepassword")

	server := server{authenticator: &dummyAuth{ret: true}, config: configuration{SSHKey: "testdata/id_rsa"}}
	server.keyHandler(response, request)

	code := response.Code
	if code != 200 {
		t.Error("http status should be 200:", code)
	}
}

func TestKeysHandlerAuthenticatesTheRequest(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	request.SetBasicAuth("keyguard", "supersecurepassword")

	auth := &dummyAuth{ret: true}
	server := server{authenticator: auth}
	server.keyHandler(response, request)

	if auth.called != 1 {
		t.Error("the authenticator was not called")
	}

	if auth.usernameSent != "keyguard" {
		t.Error("sent the wrong username to the authenticator:", auth.usernameSent)
	}

	if auth.passwordSent != "supersecurepassword" {
		t.Error("sent the wrong password to the authenticator:", auth.passwordSent)
	}
}

func TestKeysHandlerRespondsWithKey(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	request.SetBasicAuth("cromega", "supersecurepassword")

	server := server{config: configuration{SSHKey: "testdata/id_rsa"}, authenticator: &dummyAuth{ret: true}}
	server.keyHandler(response, request)

	body := response.Body.String()
	if body != "awesome private key" {
		t.Error("server should have responded with the correct ssh key:", body)
	}
}

func TestPublicKeyHandlerSendsPublicKey(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/pubkey", nil)

	server := server{config: configuration{SSHKey: "testdata/real_id_rsa"}}
	server.pubKeyHandler(response, request)

	if response.Code != 200 {
		t.Error("response should be OK", response.Code)
	}

	body := response.Body.String()
	if body != "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDBgps60FUrGlCFlH48/cSWaDuZYfCTvgRIZt60CiQ1fPj53SQ6xpcDpSCpt1pJt/Q1xZtHPaNZ+HWKAU3tOgspi/AJdrQAPC54CLzdBsMlL/+JxjMxtCf0bbG8dxoRijxIppXVyIuCLabA2oEhepf3U/H+Dvm3XST22f87FsQVrw==\n" {
		t.Error("server should have responded with a public key:", body)
	}
}
<commit_msg>fix false positive<commit_after>package main

import (
	"net/http"
	"net/http/httptest"
	"testing"
)

type dummyAuth struct {
	usernameSent, passwordSent string
	called                     int
	ret                        bool
}

func (a *dummyAuth) authenticate(username, password string) (bool, error) {
	a.usernameSent = username
	a.passwordSent = password
	a.called += 1
	return a.ret, nil
}

func TestRootHandlerServesLoaderScript(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/", nil)

	server := server{config: configuration{LoaderScript: "testdata/loader.sh"}}
	server.rootHandler(response, request)

	code := response.Code
	if code != 200 {
		t.Error("response code was not 200:", code)
	}

	body := response.Body.String()
	if body != "awesome loader script" {
		t.Error("wrong response from / endpoint:", body)
	}
}

func TestKeysHandlerRequiresAuthentication(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	server := server{authenticator: &dummyAuth{}, config: configuration{SSHKey: "testdata/id_rsa"}}
	server.keyHandler(response, request)

	code := response.Code
	if code != 401 {
		t.Error("request should have been rejected with 401:", code)
	}

	header := response.Header().Get("Authenticate")
	if header != "KeyGuard" {
		t.Error("correct authenticate header was not in response:", header)
	}
}

func TestKeysHandlerRequiresValidCredentials(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	request.SetBasicAuth("cromega", "supersecurepassword")

	server := server{authenticator: &dummyAuth{ret: true}, config: configuration{SSHKey: "testdata/id_rsa"}}
	server.keyHandler(response, request)

	code := response.Code
	if code != 200 {
		t.Error("http status should be 200:", code)
	}
}

func TestKeysHandlerAuthenticatesTheRequest(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	request.SetBasicAuth("keyguard", "supersecurepassword")

	auth := &dummyAuth{ret: true}
	server := server{authenticator: auth, config: configuration{SSHKey: "testdata/id_rsa"}}
	server.keyHandler(response, request)

	if auth.called != 1 {
		t.Error("the authenticator was not called")
	}

	if auth.usernameSent != "keyguard" {
		t.Error("sent the wrong username to the authenticator:", auth.usernameSent)
	}

	if auth.passwordSent != "supersecurepassword" {
		t.Error("sent the wrong password to the authenticator:", auth.passwordSent)
	}

	if response.Code != 200 {
		t.Error("wrong respnse code:", response.Code)
	}
}

func TestKeysHandlerRespondsWithKey(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/key", nil)

	request.SetBasicAuth("cromega", "supersecurepassword")

	server := server{config: configuration{SSHKey: "testdata/id_rsa"}, authenticator: &dummyAuth{ret: true}}
	server.keyHandler(response, request)

	body := response.Body.String()
	if body != "awesome private key" {
		t.Error("server should have responded with the correct ssh key:", body)
	}
}

func TestPublicKeyHandlerSendsPublicKey(t *testing.T) {
	response := httptest.NewRecorder()
	request, _ := http.NewRequest("GET", "/pubkey", nil)

	server := server{config: configuration{SSHKey: "testdata/real_id_rsa"}}
	server.pubKeyHandler(response, request)

	if response.Code != 200 {
		t.Error("response should be OK", response.Code)
	}

	body := response.Body.String()
	if body != "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDBgps60FUrGlCFlH48/cSWaDuZYfCTvgRIZt60CiQ1fPj53SQ6xpcDpSCpt1pJt/Q1xZtHPaNZ+HWKAU3tOgspi/AJdrQAPC54CLzdBsMlL/+JxjMxtCf0bbG8dxoRijxIppXVyIuCLabA2oEhepf3U/H+Dvm3XST22f87FsQVrw==\n" {
		t.Error("server should have responded with a public key:", body)
	}
}
<|endoftext|>