<commit_before>package stackit

import (
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws"
	"log"
	"fmt"
	"time"
	"os"
	"os/signal"
	"encoding/json"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"strings"
	"github.com/fatih/color"
)

type StackitUpInput struct {
	StackName *string
	RoleARN *string
	StackPolicyBody *string
	TemplateBody *string
	PreviousTemplate *bool
	Parameters []*cloudformation.Parameter
	Tags []*cloudformation.Tag
	NotificationARNs []*string
	Capabilities []*string
}

func CfnClient(profile, region string) *cloudformation.CloudFormation {
	sessOpts := session.Options{
		SharedConfigState: session.SharedConfigEnable,
		AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
	}

	if len(profile) > 0 {
		sessOpts.Profile = profile
	}

	sess, _ := session.NewSessionWithOptions(sessOpts)
	config := aws.NewConfig()

	if len(region) > 0 {
		config.Region = aws.String(region)
	}

	return cloudformation.New(sess, config)
}

func CancelOnInterrupt(stackId *string, isNewStackCreation bool, cfn *cloudformation.CloudFormation) {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, os.Interrupt)

	go func() {
		<- sigs

		if isNewStackCreation {
			cfn.DeleteStack(&cloudformation.DeleteStackInput{
				StackName: stackId,
			})
		} else {
			cfn.CancelUpdateStack(&cloudformation.CancelUpdateStackInput{
				StackName: stackId,
			})
		}
	}()
}

func PrintOutputs(stackId *string, cfn *cloudformation.CloudFormation) {
	resp, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{
		StackName: stackId,
	})

	if err != nil {
		log.Fatal(err.Error())
	}

	outputMap := make(map[string]string)

	for _, output := range resp.Stacks[0].Outputs {
		outputMap[*output.OutputKey] = *output.OutputValue
	}

	bytes, err := json.MarshalIndent(outputMap, "", "  ")
	fmt.Println(string(bytes))
}

func Down(region, profile, stackName string) (*string, *string) {
	cfn := CfnClient(profile, region)

	if stackExists(&stackName, cfn) {
		resp, err := cfn.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
			StackName: &stackName,
		})

		if err != nil {
			log.Fatal(err.Error())
		}

		stackId := resp.StackEvents[0].StackId
		mostRecentEventIdSeen := resp.StackEvents[0].EventId

		cfn.DeleteStack(&cloudformation.DeleteStackInput{
			StackName: &stackName,
		})

		return stackId, mostRecentEventIdSeen

	}

	return nil, nil
}

func stackExists(stackName *string, cfn *cloudformation.CloudFormation) bool {
	_, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{
		StackName: stackName,
	})

	return err == nil
}

func fixedLengthString(length int, str string) string {
	verb := fmt.Sprintf("%%%d.%ds", length, length)
	return fmt.Sprintf(verb, str)
}

func isBadStatus(status string) bool {
	return strings.HasSuffix(status, "_FAILED")
}

func TailStack(stackId, mostRecentEventIdSeen *string, showTimestamps, showColor bool, cfn *cloudformation.CloudFormation) string {
	if mostRecentEventIdSeen == nil {
		tmp := ""
		mostRecentEventIdSeen = &tmp
	}

	resourceNameLength := 20 // TODO: determine this from template/API

	for {
		time.Sleep(3*time.Second)

		events := []*cloudformation.StackEvent{}

		cfn.DescribeStackEventsPages(&cloudformation.DescribeStackEventsInput{
			StackName: stackId,
		}, func(page *cloudformation.DescribeStackEventsOutput, lastPage bool) bool {
			for _, event := range page.StackEvents {
				if *event.EventId == *mostRecentEventIdSeen {
					return false
				}

				events = append(events, event)
			}
			return true
		})

		if len(events) == 0 {
			continue
		}

		mostRecentEventIdSeen = events[0].EventId

		for ev_i := len(events) - 1; ev_i >= 0; ev_i-- {
			event := events[ev_i]

			timestampPrefix := ""
			if showTimestamps {
				timestampPrefix = event.Timestamp.Format("[03:04:05]")
			}

			reasonPart := ""
			if event.ResourceStatusReason != nil {
				reasonPart = fmt.Sprintf("- %s", *event.ResourceStatusReason)
			}

			line := fmt.Sprintf("%s %s - %s %s", timestampPrefix, fixedLengthString(resourceNameLength, *event.LogicalResourceId), *event.ResourceStatus, reasonPart)
			if showColor && isBadStatus(*event.ResourceStatus) {
				color.New(color.FgRed).Fprintln(os.Stderr, line)
			} else {
				fmt.Fprintln(os.Stderr, line)
			}
		}

		resp, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{StackName: stackId})

		if err != nil {
			log.Fatal(err.Error())
		}

		status := *resp.Stacks[0].StackStatus
		switch status {
		case
			"CREATE_COMPLETE",
			"DELETE_COMPLETE",
			"CREATE_FAILED",
			"DELETE_FAILED",
			"ROLLBACK_COMPLETE",
			"ROLLBACK_FAILED",
			"UPDATE_COMPLETE",
			"UPDATE_FAILED",
			"UPDATE_ROLLBACK_COMPLETE",
			"UPDATE_ROLLBACK_FAILED":
			return status
		default:
			// no-op
		}
	}
}

func Up(input StackitUpInput, cfn *cloudformation.CloudFormation) (*string, *string, error) {
	if stackExists(input.StackName, cfn) {
		describeResp, err := cfn.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
			StackName: input.StackName,
		})

		if err != nil {
			return nil, nil, err
		}
		_, err = cfn.UpdateStack(&cloudformation.UpdateStackInput{
			StackName: input.StackName,
			Capabilities: input.Capabilities,
			RoleARN: input.RoleARN,
			StackPolicyDuringUpdateBody: input.StackPolicyBody,
			TemplateBody: input.TemplateBody,
			UsePreviousTemplate: input.PreviousTemplate,
			Parameters: input.Parameters,
			Tags: input.Tags,
			NotificationARNs: input.NotificationARNs,
		})

		event := describeResp.StackEvents[0]
		eventIdToTail := event.EventId

		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				if awsErr.Code() == "ValidationError" && awsErr.Message() == "No updates are to be performed." {
					eventIdToTail = nil
					err = nil
				}
			}
		}

		return event.StackId, eventIdToTail, err
	} else {
		resp, err := cfn.CreateStack(&cloudformation.CreateStackInput{
			StackName: input.StackName,
			Capabilities: input.Capabilities,
			RoleARN: input.RoleARN,
			//StackPolicyBody: input.StackPolicyBody,
			TemplateBody: input.TemplateBody,
			Parameters: input.Parameters,
			Tags: input.Tags,
			NotificationARNs: input.NotificationARNs,
		})

		if err != nil {
			return nil, nil, err
		} else {
			blank := ""
			return resp.StackId, &blank, nil
		}
	}
}
<commit_msg>Delete a good-for-nothing zombie stack before doing a stack up<commit_after>package stackit

import (
	"github.com/aws/aws-sdk-go/service/cloudformation"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/aws/credentials/stscreds"
	"github.com/aws/aws-sdk-go/aws"
	"log"
	"fmt"
	"time"
	"os"
	"os/signal"
	"encoding/json"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"strings"
	"github.com/fatih/color"
)

type StackitUpInput struct {
	StackName *string
	RoleARN *string
	StackPolicyBody *string
	TemplateBody *string
	PreviousTemplate *bool
	Parameters []*cloudformation.Parameter
	Tags []*cloudformation.Tag
	NotificationARNs []*string
	Capabilities []*string
}

func CfnClient(profile, region string) *cloudformation.CloudFormation {
	sessOpts := session.Options{
		SharedConfigState: session.SharedConfigEnable,
		AssumeRoleTokenProvider: stscreds.StdinTokenProvider,
	}

	if len(profile) > 0 {
		sessOpts.Profile = profile
	}

	sess, _ := session.NewSessionWithOptions(sessOpts)
	config := aws.NewConfig()

	if len(region) > 0 {
		config.Region = aws.String(region)
	}

	return cloudformation.New(sess, config)
}

func CancelOnInterrupt(stackId *string, isNewStackCreation bool, cfn *cloudformation.CloudFormation) {
	sigs := make(chan os.Signal, 1)
	signal.Notify(sigs, os.Interrupt)

	go func() {
		<- sigs

		if isNewStackCreation {
			cfn.DeleteStack(&cloudformation.DeleteStackInput{
				StackName: stackId,
			})
		} else {
			cfn.CancelUpdateStack(&cloudformation.CancelUpdateStackInput{
				StackName: stackId,
			})
		}
	}()
}

func PrintOutputs(stackId *string, cfn *cloudformation.CloudFormation) {
	resp, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{
		StackName: stackId,
	})

	if err != nil {
		log.Fatal(err.Error())
	}

	outputMap := make(map[string]string)

	for _, output := range resp.Stacks[0].Outputs {
		outputMap[*output.OutputKey] = *output.OutputValue
	}

	bytes, err := json.MarshalIndent(outputMap, "", "  ")
	fmt.Println(string(bytes))
}

func Down(region, profile, stackName string) (*string, *string) {
	cfn := CfnClient(profile, region)

	_, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{StackName: &stackName})
	stackExists := err == nil

	if stackExists {
		resp, err := cfn.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
			StackName: &stackName,
		})

		if err != nil {
			log.Fatal(err.Error())
		}

		stackId := resp.StackEvents[0].StackId
		mostRecentEventIdSeen := resp.StackEvents[0].EventId

		cfn.DeleteStack(&cloudformation.DeleteStackInput{
			StackName: &stackName,
		})

		return stackId, mostRecentEventIdSeen

	}

	return nil, nil
}

func fixedLengthString(length int, str string) string {
	verb := fmt.Sprintf("%%%d.%ds", length, length)
	return fmt.Sprintf(verb, str)
}

func isBadStatus(status string) bool {
	return strings.HasSuffix(status, "_FAILED")
}

func TailStack(stackId, mostRecentEventIdSeen *string, showTimestamps, showColor bool, cfn *cloudformation.CloudFormation) string {
	if mostRecentEventIdSeen == nil {
		tmp := ""
		mostRecentEventIdSeen = &tmp
	}

	resourceNameLength := 20 // TODO: determine this from template/API

	for {
		time.Sleep(3*time.Second)

		events := []*cloudformation.StackEvent{}

		cfn.DescribeStackEventsPages(&cloudformation.DescribeStackEventsInput{
			StackName: stackId,
		}, func(page *cloudformation.DescribeStackEventsOutput, lastPage bool) bool {
			for _, event := range page.StackEvents {
				if *event.EventId == *mostRecentEventIdSeen {
					return false
				}

				events = append(events, event)
			}
			return true
		})

		if len(events) == 0 {
			continue
		}

		mostRecentEventIdSeen = events[0].EventId

		for ev_i := len(events) - 1; ev_i >= 0; ev_i-- {
			event := events[ev_i]

			timestampPrefix := ""
			if showTimestamps {
				timestampPrefix = event.Timestamp.Format("[03:04:05]")
			}

			reasonPart := ""
			if event.ResourceStatusReason != nil {
				reasonPart = fmt.Sprintf("- %s", *event.ResourceStatusReason)
			}

			line := fmt.Sprintf("%s %s - %s %s", timestampPrefix, fixedLengthString(resourceNameLength, *event.LogicalResourceId), *event.ResourceStatus, reasonPart)
			if showColor && isBadStatus(*event.ResourceStatus) {
				color.New(color.FgRed).Fprintln(os.Stderr, line)
			} else {
				fmt.Fprintln(os.Stderr, line)
			}
		}

		resp, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{StackName: stackId})

		if err != nil {
			log.Fatal(err.Error())
		}

		status := *resp.Stacks[0].StackStatus
		switch status {
		case
			"CREATE_COMPLETE",
			"DELETE_COMPLETE",
			"CREATE_FAILED",
			"DELETE_FAILED",
			"ROLLBACK_COMPLETE",
			"ROLLBACK_FAILED",
			"UPDATE_COMPLETE",
			"UPDATE_FAILED",
			"UPDATE_ROLLBACK_COMPLETE",
			"UPDATE_ROLLBACK_FAILED":
			return status
		default:
			// no-op
		}
	}
}

func Up(input StackitUpInput, cfn *cloudformation.CloudFormation) (*string, *string, error) {
	resp, err := cfn.DescribeStacks(&cloudformation.DescribeStacksInput{StackName: input.StackName})
	stackExists := err == nil

	if resp != nil && len(resp.Stacks) > 0 {
		stack := resp.Stacks[0]
		if *stack.StackStatus == "CREATE_FAILED" || *stack.StackStatus == "ROLLBACK_COMPLETE" {
			cfn.DeleteStack(&cloudformation.DeleteStackInput{StackName: input.StackName})
			stackExists = false
		}
	}

	if stackExists {
		describeResp, err := cfn.DescribeStackEvents(&cloudformation.DescribeStackEventsInput{
			StackName: input.StackName,
		})

		if err != nil {
			return nil, nil, err
		}
		_, err = cfn.UpdateStack(&cloudformation.UpdateStackInput{
			StackName: input.StackName,
			Capabilities: input.Capabilities,
			RoleARN: input.RoleARN,
			StackPolicyDuringUpdateBody: input.StackPolicyBody,
			TemplateBody: input.TemplateBody,
			UsePreviousTemplate: input.PreviousTemplate,
			Parameters: input.Parameters,
			Tags: input.Tags,
			NotificationARNs: input.NotificationARNs,
		})

		event := describeResp.StackEvents[0]
		eventIdToTail := event.EventId

		if err != nil {
			if awsErr, ok := err.(awserr.Error); ok {
				if awsErr.Code() == "ValidationError" && awsErr.Message() == "No updates are to be performed." {
					eventIdToTail = nil
					err = nil
				}
			}
		}

		return event.StackId, eventIdToTail, err
	} else {
		resp, err := cfn.CreateStack(&cloudformation.CreateStackInput{
			StackName: input.StackName,
			Capabilities: input.Capabilities,
			RoleARN: input.RoleARN,
			//StackPolicyBody: input.StackPolicyBody,
			TemplateBody: input.TemplateBody,
			Parameters: input.Parameters,
			Tags: input.Tags,
			NotificationARNs: input.NotificationARNs,
		})

		if err != nil {
			return nil, nil, err
		} else {
			blank := ""
			return resp.StackId, &blank, nil
		}
	}
}
<|endoftext|>


<commit_before>package raven

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func init() {
	thisFile, thisPackage = derivePackage()
	functionNameTests = []FunctionNameTest{
		{0, thisPackage, "TestFunctionName"},
		{1, "testing", "tRunner"},
		{2, "runtime", "goexit"},
		{100, "", ""},
	}
}

type FunctionNameTest struct {
	skip int
	pack string
	name string
}

var (
	thisFile          string
	thisPackage       string
	functionNameTests []FunctionNameTest
)

func TestFunctionName(t *testing.T) {
	for _, test := range functionNameTests {
		pc, _, _, _ := runtime.Caller(test.skip)
		pack, name := functionName(pc)

		if pack != test.pack {
			t.Errorf("incorrect package; got %s, want %s", pack, test.pack)
		}
		if name != test.name {
			t.Errorf("incorrect function; got %s, want %s", name, test.name)
		}
	}
}

func TestSplitFunctionName(t *testing.T) {
	tests := []struct{
		in string
		pack, name string
	}{
		{"", "", ""},
		{"main.main", "main", "main"},
		{"main.main.func1", "main", "main.func1"},
		{"cmd/main.main", "cmd/main", "main"},
		{"cmd/main.main.func1", "cmd/main", "main.func1"},
		{"cmd/main.main.func1.1", "cmd/main", "main.func1.1"},
		{"cmd/main.main.func1.1.1", "cmd/main", "main.func1.1.1"},
		{"cmd/main.(*T).Do", "cmd/main", "(*T).Do"},
		{"cmd/main.*T.Do", "cmd/main", "*T.Do"},
	}

	for _, test := range tests {
		pack, name := splitFunctionName(test.in)
		if pack != test.pack || name != test.name {
			t.Errorf("wrong answer from splitFunctionName(%q); got (%q, %q), but want (%q, %q)", test.in, test.pack, test.name, pack, name)
		}
	}
}

func TestStacktrace(t *testing.T) {
	st := trace()
	if st == nil {
		t.Error("got nil stacktrace")
	}
	if len(st.Frames) == 0 {
		t.Error("got zero frames")
	}

	f := st.Frames[len(st.Frames)-1]
	if f.Filename != thisFile {
		t.Errorf("incorrect Filename; got %s, want %s", f.Filename, thisFile)
	}
	if !strings.HasSuffix(f.AbsolutePath, thisFile) {
		t.Error("incorrect AbsolutePath:", f.AbsolutePath)
	}
	if f.Function != "trace" {
		t.Error("incorrect Function:", f.Function)
	}
	if f.Module != thisPackage {
		t.Error("incorrect Module:", f.Module)
	}
	if f.Lineno != 121 {
		t.Error("incorrect Lineno:", f.Lineno)
	}
	if f.ContextLine != "\treturn NewStacktrace(0, 2, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
	if len(f.PreContext) != 2 || f.PreContext[0] != "// a" || f.PreContext[1] != "func trace() *Stacktrace {" {
		t.Errorf("incorrect PreContext %#v", f.PreContext)
	}
	if len(f.PostContext) != 2 || f.PostContext[0] != "\t// b" || f.PostContext[1] != "}" {
		t.Errorf("incorrect PostContext %#v", f.PostContext)
	}
	_, filename, _, _ := runtime.Caller(0)
	runningInVendored := strings.Contains(filename, "vendor")
	if f.InApp != !runningInVendored {
		t.Error("expected InApp to be true")
	}

	if f.InApp && st.Culprit() != fmt.Sprintf("%s.trace", thisPackage) {
		t.Error("incorrect Culprit:", st.Culprit())
	}
}

// a
func trace() *Stacktrace {
	return NewStacktrace(0, 2, []string{thisPackage})
	// b
}

func derivePackage() (file, pack string) {
	// Get file name by seeking caller's file name.
	_, callerFile, _, ok := runtime.Caller(1)
	if !ok {
		return
	}

	// Trim file name
	file = callerFile
	for _, dir := range build.Default.SrcDirs() {
		dir := dir + string(filepath.Separator)
		if trimmed := strings.TrimPrefix(callerFile, dir); len(trimmed) < len(file) {
			file = trimmed
		}
	}

	// Now derive package name
	dir := filepath.Dir(callerFile)

	dirPkg, err := build.ImportDir(dir, build.AllowBinary)
	if err != nil {
		return
	}

	pack = dirPkg.ImportPath
	return
}

// TestNewStacktrace_outOfBounds verifies that a context exceeding the number
// of lines in a file does not cause a panic.
func TestNewStacktrace_outOfBounds(t *testing.T) {
	st := NewStacktrace(0, 1000000, []string{thisPackage})
	f := st.Frames[len(st.Frames)-1]
	if f.ContextLine != "\tst := NewStacktrace(0, 1000000, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
}

func TestNewStacktrace_noFrames(t *testing.T) {
	st := NewStacktrace(999999999, 0, []string{})
	if st != nil {
		t.Errorf("expected st.Frames to be nil: %v", st)
	}
}

func TestFileContext(t *testing.T) {
	// reset the cache
	fileCache = make(map[string][][]byte)

	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal("failed to create temporary directory:", err)
	}
	defer os.RemoveAll(tempdir)

	okPath := filepath.Join(tempdir, "ok")
	missingPath := filepath.Join(tempdir, "missing")
	noPermissionPath := filepath.Join(tempdir, "noperms")

	err = ioutil.WriteFile(okPath, []byte("hello\nworld\n"), 0600)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}
	err = ioutil.WriteFile(noPermissionPath, []byte("no access\n"), 0000)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}

	tests := []struct {
		path          string
		expectedLines int
		expectedIndex int
	}{
		{okPath, 1, 0},
		{missingPath, 0, 0},
		{noPermissionPath, 0, 0},
	}
	for i, test := range tests {
		lines, index := fileContext(test.path, 1, 0)
		if !(len(lines) == test.expectedLines && index == test.expectedIndex) {
			t.Errorf("%d: fileContext(%#v, 1, 0) = %v, %v; expected len()=%d, %d",
				i, test.path, lines, index, test.expectedLines, test.expectedIndex)
		}
		if len(fileCache) != i+1 {
			t.Errorf("%d: result was not cached; len(fileCached)=%d", i, len(fileCache))
		}
	}
}
<commit_msg>Fix test<commit_after>package raven

import (
	"fmt"
	"go/build"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

func init() {
	thisFile, thisPackage = derivePackage()
	functionNameTests = []FunctionNameTest{
		{0, thisPackage, "TestFunctionName"},
		{1, "testing", "tRunner"},
		{2, "runtime", "goexit"},
		{100, "", ""},
	}
}

type FunctionNameTest struct {
	skip int
	pack string
	name string
}

var (
	thisFile          string
	thisPackage       string
	functionNameTests []FunctionNameTest
)

func TestFunctionName(t *testing.T) {
	for _, test := range functionNameTests {
		pc, _, _, _ := runtime.Caller(test.skip)
		pack, name := functionName(pc)

		if pack != test.pack {
			t.Errorf("incorrect package; got %s, want %s", pack, test.pack)
		}
		if name != test.name {
			t.Errorf("incorrect function; got %s, want %s", name, test.name)
		}
	}
}

func TestSplitFunctionName(t *testing.T) {
	tests := []struct {
		in         string
		pack, name string
	}{
		{"", "", ""},
		{"main.main", "main", "main"},
		{"main.main.func1", "main", "main.func1"},
		{"cmd/main.main", "cmd/main", "main"},
		{"cmd/main.main.func1", "cmd/main", "main.func1"},
		{"cmd/main.main.func1.1", "cmd/main", "main.func1.1"},
		{"cmd/main.main.func1.1.1", "cmd/main", "main.func1.1.1"},
		{"cmd/main.(*T).Do", "cmd/main", "(*T).Do"},
		{"cmd/main.*T.Do", "cmd/main", "*T.Do"},
	}

	for _, test := range tests {
		pack, name := splitFunctionName(test.in)
		if pack != test.pack || name != test.name {
			t.Errorf("wrong answer from splitFunctionName(%q); got (%q, %q), but want (%q, %q)", test.in, test.pack, test.name, pack, name)
		}
	}
}

func TestStacktrace(t *testing.T) {
	st := trace()
	if st == nil {
		t.Error("got nil stacktrace")
	}
	if len(st.Frames) == 0 {
		t.Error("got zero frames")
	}

	f := st.Frames[len(st.Frames)-1]
	if f.Filename != thisFile {
		t.Errorf("incorrect Filename; got %s, want %s", f.Filename, thisFile)
	}
	if !strings.HasSuffix(f.AbsolutePath, thisFile) {
		t.Error("incorrect AbsolutePath:", f.AbsolutePath)
	}
	if f.Function != "trace" {
		t.Error("incorrect Function:", f.Function)
	}
	if f.Module != thisPackage {
		t.Error("incorrect Module:", f.Module)
	}
	if f.Lineno != 121 {
		t.Error("incorrect Lineno:", f.Lineno)
	}
	if f.ContextLine != "\treturn NewStacktrace(0, 2, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
	if len(f.PreContext) != 2 || f.PreContext[0] != "// a" || f.PreContext[1] != "func trace() *Stacktrace {" {
		t.Errorf("incorrect PreContext %#v", f.PreContext)
	}
	if len(f.PostContext) != 2 || f.PostContext[0] != "\t// b" || f.PostContext[1] != "}" {
		t.Errorf("incorrect PostContext %#v", f.PostContext)
	}
	_, filename, _, _ := runtime.Caller(0)
	runningInVendored := strings.Contains(filename, "vendor")
	if f.InApp != !runningInVendored {
		t.Error("expected InApp to be true")
	}

	if f.InApp && st.Culprit() != fmt.Sprintf("%s.trace", thisPackage) {
		t.Error("incorrect Culprit:", st.Culprit())
	}
}

// a
func trace() *Stacktrace {
	return NewStacktrace(0, 2, []string{thisPackage})
	// b
}

func derivePackage() (file, pack string) {
	// Get file name by seeking caller's file name.
	_, callerFile, _, ok := runtime.Caller(1)
	if !ok {
		return
	}

	// Trim file name
	file = callerFile
	for _, dir := range build.Default.SrcDirs() {
		dir := dir + string(filepath.Separator)
		if trimmed := strings.TrimPrefix(callerFile, dir); len(trimmed) < len(file) {
			file = trimmed
		}
	}

	// Now derive package name
	dir := filepath.Dir(callerFile)

	dirPkg, err := build.ImportDir(dir, build.AllowBinary)
	if err != nil {
		return
	}

	pack = dirPkg.ImportPath
	return
}

// TestNewStacktrace_outOfBounds verifies that a context exceeding the number
// of lines in a file does not cause a panic.
func TestNewStacktrace_outOfBounds(t *testing.T) {
	st := NewStacktrace(0, 1000000, []string{thisPackage})
	f := st.Frames[len(st.Frames)-1]
	if f.ContextLine != "\tst := NewStacktrace(0, 1000000, []string{thisPackage})" {
		t.Errorf("incorrect ContextLine: %#v", f.ContextLine)
	}
}

func TestNewStacktrace_noFrames(t *testing.T) {
	st := NewStacktrace(999999999, 0, []string{})
	if st != nil {
		t.Errorf("expected st.Frames to be nil: %v", st)
	}
}

func TestFileContext(t *testing.T) {
	// reset the cache
	sourceCodeLoader = fsLoader{cache: make(map[string][][]byte)}

	tempdir, err := ioutil.TempDir("", "")
	if err != nil {
		t.Fatal("failed to create temporary directory:", err)
	}
	defer os.RemoveAll(tempdir)

	okPath := filepath.Join(tempdir, "ok")
	missingPath := filepath.Join(tempdir, "missing")
	noPermissionPath := filepath.Join(tempdir, "noperms")

	err = ioutil.WriteFile(okPath, []byte("hello\nworld\n"), 0600)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}
	err = ioutil.WriteFile(noPermissionPath, []byte("no access\n"), 0000)
	if err != nil {
		t.Fatal("failed writing file:", err)
	}

	tests := []struct {
		path          string
		expectedLines int
		expectedIndex int
	}{
		{okPath, 1, 0},
		{missingPath, 0, 0},
		{noPermissionPath, 0, 0},
	}
	for i, test := range tests {
		lines, index := sourceCodeLoader.Load(test.path, 1, 0)
		if !(len(lines) == test.expectedLines && index == test.expectedIndex) {
			t.Errorf("%d: fileContext(%#v, 1, 0) = %v, %v; expected len()=%d, %d",
				i, test.path, lines, index, test.expectedLines, test.expectedIndex)
		}
		cacheLen := len(sourceCodeLoader.(fsLoader).cache)
		if cacheLen != i+1 {
			t.Errorf("%d: result was not cached; len=%d", i, cacheLen)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Jonathan Chappelow
// See LICENSE for details.

package stakedb

import (
	"encoding/binary"
	"fmt"
	"os"
	"sync"

	"github.com/dcrdata/dcrdata/rpcutils"
	"github.com/dcrdata/dcrdata/txhelpers"
	"github.com/decred/dcrd/blockchain/stake"
	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/database"
	"github.com/decred/dcrrpcclient"
	"github.com/decred/dcrutil"
)

type StakeDatabase struct {
	params     *chaincfg.Params
	NodeClient *dcrrpcclient.Client
	nodeMtx    sync.RWMutex
	StakeDB    database.DB
	BestNode   *stake.Node
	blkMtx     sync.RWMutex
	blockCache map[int64]*dcrutil.Block
}

const (
	// dbType is the database backend type to use
	dbType = "ffldb"
	// DefaultStakeDbName is the default database name
	DefaultStakeDbName = "ffldb_stake"
)

func NewStakeDatabase(client *dcrrpcclient.Client, params *chaincfg.Params) (*StakeDatabase, error) {
	sDB := &StakeDatabase{
		params:     params,
		NodeClient: client,
		blockCache: make(map[int64]*dcrutil.Block),
	}
	if err := sDB.Open(); err != nil {
		return nil, err
	}
	return sDB, nil
}

func (db *StakeDatabase) Height() uint32 {
	if db == nil || db.BestNode == nil {
		log.Error("Stake database not yet opened")
		return 0
	}
	db.nodeMtx.RLock()
	defer db.nodeMtx.RUnlock()
	return db.BestNode.Height()
}

func (db *StakeDatabase) Block(ind int64) (*dcrutil.Block, bool) {
	db.blkMtx.RLock()
	block, ok := db.blockCache[ind]
	db.blkMtx.RUnlock()
	//log.Info(ind, block, ok)
	if !ok {
		var err error
		block, _, err = rpcutils.GetBlock(ind, db.NodeClient)
		if err != nil {
			log.Error(err)
			return nil, false
		}
	}
	return block, ok
}

func (db *StakeDatabase) ForgetBlock(ind int64) {
	db.blkMtx.Lock()
	defer db.blkMtx.Unlock()
	delete(db.blockCache, ind)
}

func (db *StakeDatabase) ConnectBlockHeight(height int64) (*dcrutil.Block, error) {
	block, _ := db.Block(height)
	return block, db.ConnectBlock(block)
}

func (db *StakeDatabase) ConnectBlockHash(hash *chainhash.Hash) (*dcrutil.Block, error) {
	block, err := db.NodeClient.GetBlock(hash)
	if err != nil {
		return nil, err
	}
	return block, db.ConnectBlock(block)
}

func (db *StakeDatabase) ConnectBlock(block *dcrutil.Block) error {
	height := block.Height()
	maturingHeight := height - int64(db.params.TicketMaturity)
	//log.Info("Connect ", height, maturingHeight)
	var maturingTickets []chainhash.Hash
	if maturingHeight >= 0 {
		maturingBlock, wasCached := db.Block(maturingHeight)
		if wasCached {
			db.ForgetBlock(maturingHeight)
		}
		maturingTickets = txhelpers.TicketsInBlock(maturingBlock)
	}

	db.blkMtx.Lock()
	db.blockCache[block.Height()] = block
	db.blkMtx.Unlock()

	revokedTickets := txhelpers.RevokedTicketsInBlock(block)
	spentTickets := txhelpers.TicketsSpentInBlock(block)

	//log.Info("Connect ", len(revokedTickets), len(spentTickets), len(maturingTickets))

	if int64(db.Height()+1) != height {
		panic(fmt.Sprintf("trying to connect the wrong next block: %d, %d", height, db.Height()))
	}

	return db.connectBlock(block, spentTickets, revokedTickets, maturingTickets)
}

func (db *StakeDatabase) connectBlock(block *dcrutil.Block, spent []chainhash.Hash,
	revoked []chainhash.Hash, maturing []chainhash.Hash) error {
	db.nodeMtx.Lock()
	defer db.nodeMtx.Unlock()

	var err error
	db.BestNode, err = db.BestNode.ConnectNode(block.MsgBlock().Header,
		spent, revoked, maturing)
	if err != nil {
		return err
	}

	// Write the new node to db
	return db.StakeDB.Update(func(dbTx database.Tx) error {
		return stake.WriteConnectedBestNode(dbTx, db.BestNode, *block.Hash())
	})
}

func (db *StakeDatabase) DisconnectBlock() error {
	db.nodeMtx.Lock()
	defer db.nodeMtx.Unlock()

	formerBestNode := db.BestNode
	parentBlock, _ := db.Block(int64(db.Height()) - 1)
	if parentBlock == nil {
		return fmt.Errorf("Unable to get parent block")
	}

	// previous best node
	err := db.StakeDB.View(func(dbTx database.Tx) error {
		var errLocal error
		db.BestNode, errLocal = db.BestNode.DisconnectNode(
			parentBlock.MsgBlock().Header, nil, nil, dbTx)
		return errLocal
	})
	if err != nil {
		return err
	}

	return db.StakeDB.Update(func(dbTx database.Tx) error {
		return stake.WriteDisconnectedBestNode(dbTx, db.BestNode,
			*parentBlock.Hash(), formerBestNode.UndoData())
	})
}

func (db *StakeDatabase) Open() error {
	db.nodeMtx.Lock()
	defer db.nodeMtx.Unlock()

	// Create a new database to store the accepted stake node data into.
	dbName := DefaultStakeDbName
	var err error
	db.StakeDB, err = database.Open(dbType, dbName, db.params.Net)
	if err != nil {
		log.Infof("Unable to open stake DB (%v). Removing and creating new.", err)
		_ = os.RemoveAll(dbName)
		db.StakeDB, err = database.Create(dbType, dbName, db.params.Net)
		if err != nil {
			// do not return nil interface, but interface of nil DB
			return fmt.Errorf("error creating db: %v", err)
		}
	}

	// Load the best block from stake db
	var bestNodeHeight = int64(-1)
	err = db.StakeDB.View(func(dbTx database.Tx) error {
		v := dbTx.Metadata().Get([]byte("stakechainstate"))
		if v == nil {
			return fmt.Errorf("missing key for chain state data")
		}

		var stakeDBHash chainhash.Hash
		copy(stakeDBHash[:], v[:chainhash.HashSize])
		offset := chainhash.HashSize
		stakeDBHeight := binary.LittleEndian.Uint32(v[offset : offset+4])
		bestNodeHeight = int64(stakeDBHeight)

		var errLocal error
		block, errLocal := db.NodeClient.GetBlock(&stakeDBHash)
		if errLocal != nil {
			return fmt.Errorf("GetBlock failed (%s): %v", stakeDBHash, errLocal)
		}
		header := block.MsgBlock().Header

		db.BestNode, errLocal = stake.LoadBestNode(dbTx, stakeDBHeight,
			stakeDBHash, header, db.params)
		return errLocal
	})
	if err != nil {
		log.Errorf("Error reading from database (%v).  Reinitializing.", err)
		err = db.StakeDB.Update(func(dbTx database.Tx) error {
			var errLocal error
			db.BestNode, errLocal = stake.InitDatabaseState(dbTx, db.params)
			return errLocal
		})
		log.Debug("Created new stake db.")
	} else {
		log.Debug("Opened existing stake db.")
	}

	return err
}
<commit_msg>Handle reorg in stakedb by rewinding if connecting a block at or below current db height.<commit_after>// Copyright (c) 2017, Jonathan Chappelow
// See LICENSE for details.

package stakedb

import (
	"encoding/binary"
	"fmt"
	"os"
	"sync"

	"github.com/dcrdata/dcrdata/rpcutils"
	"github.com/dcrdata/dcrdata/txhelpers"
	"github.com/decred/dcrd/blockchain/stake"
	"github.com/decred/dcrd/chaincfg"
	"github.com/decred/dcrd/chaincfg/chainhash"
	"github.com/decred/dcrd/database"
	"github.com/decred/dcrrpcclient"
	"github.com/decred/dcrutil"
)

type StakeDatabase struct {
	params     *chaincfg.Params
	NodeClient *dcrrpcclient.Client
	nodeMtx    sync.RWMutex
	StakeDB    database.DB
	BestNode   *stake.Node
	blkMtx     sync.RWMutex
	blockCache map[int64]*dcrutil.Block
}

const (
	// dbType is the database backend type to use
	dbType = "ffldb"
	// DefaultStakeDbName is the default database name
	DefaultStakeDbName = "ffldb_stake"
)

func NewStakeDatabase(client *dcrrpcclient.Client, params *chaincfg.Params) (*StakeDatabase, error) {
	sDB := &StakeDatabase{
		params:     params,
		NodeClient: client,
		blockCache: make(map[int64]*dcrutil.Block),
	}
	if err := sDB.Open(); err != nil {
		return nil, err
	}
	return sDB, nil
}

func (db *StakeDatabase) Height() uint32 {
	if db == nil || db.BestNode == nil {
		log.Error("Stake database not yet opened")
		return 0
	}
	db.nodeMtx.RLock()
	defer db.nodeMtx.RUnlock()
	return db.BestNode.Height()
}

func (db *StakeDatabase) Block(ind int64) (*dcrutil.Block, bool) {
	db.blkMtx.RLock()
	block, ok := db.blockCache[ind]
	db.blkMtx.RUnlock()
	//log.Info(ind, block, ok)
	if !ok {
		var err error
		block, _, err = rpcutils.GetBlock(ind, db.NodeClient)
		if err != nil {
			log.Error(err)
			return nil, false
		}
	}
	return block, ok
}

func (db *StakeDatabase) ForgetBlock(ind int64) {
	db.blkMtx.Lock()
	defer db.blkMtx.Unlock()
	delete(db.blockCache, ind)
}

func (db *StakeDatabase) ConnectBlockHeight(height int64) (*dcrutil.Block, error) {
	block, _ := db.Block(height)
	return block, db.ConnectBlock(block)
}

func (db *StakeDatabase) ConnectBlockHash(hash *chainhash.Hash) (*dcrutil.Block, error) {
	block, err := db.NodeClient.GetBlock(hash)
	if err != nil {
		return nil, err
	}
	return block, db.ConnectBlock(block)
}

func (db *StakeDatabase) ConnectBlock(block *dcrutil.Block) error {
	height := block.Height()
	maturingHeight := height - int64(db.params.TicketMaturity)

	var maturingTickets []chainhash.Hash
	if maturingHeight >= 0 {
		maturingBlock, wasCached := db.Block(maturingHeight)
		if wasCached {
			db.ForgetBlock(maturingHeight)
		}
		maturingTickets = txhelpers.TicketsInBlock(maturingBlock)
	}

	db.blkMtx.Lock()
	db.blockCache[block.Height()] = block
	db.blkMtx.Unlock()

	revokedTickets := txhelpers.RevokedTicketsInBlock(block)
	spentTickets := txhelpers.TicketsSpentInBlock(block)

	// If the stake db is ahead, it was probably a reorg, so rewind before
	// adding the new block
	bestNodeHeight := int64(db.Height())
	for height <= bestNodeHeight {
		log.Infof("Disconnecting block %d.", bestNodeHeight)
		if err := db.DisconnectBlock(); err != nil {
			return err
		}
		bestNodeHeight = int64(db.Height())
	}

	return db.connectBlock(block, spentTickets, revokedTickets, maturingTickets)
}

func (db *StakeDatabase) connectBlock(block *dcrutil.Block, spent []chainhash.Hash,
	revoked []chainhash.Hash, maturing []chainhash.Hash) error {
	db.nodeMtx.Lock()
	defer db.nodeMtx.Unlock()

	var err error
	db.BestNode, err = db.BestNode.ConnectNode(block.MsgBlock().Header,
		spent, revoked, maturing)
	if err != nil {
		return err
	}

	// Write the new node to db
	return db.StakeDB.Update(func(dbTx database.Tx) error {
		return stake.WriteConnectedBestNode(dbTx, db.BestNode, *block.Hash())
	})
}

func (db *StakeDatabase) DisconnectBlock() error {
	db.nodeMtx.Lock()
	defer db.nodeMtx.Unlock()

	formerBestNode := db.BestNode
	parentBlock, _ := db.Block(int64(db.Height()) - 1)
	if parentBlock == nil {
		return fmt.Errorf("Unable to get parent block")
	}

	// previous best node
	err := db.StakeDB.View(func(dbTx database.Tx) error {
		var errLocal error
		db.BestNode, errLocal = db.BestNode.DisconnectNode(
			parentBlock.MsgBlock().Header, nil, nil, dbTx)
		return errLocal
	})
	if err != nil {
		return err
	}

	return db.StakeDB.Update(func(dbTx database.Tx) error {
		return stake.WriteDisconnectedBestNode(dbTx, db.BestNode,
			*parentBlock.Hash(), formerBestNode.UndoData())
	})
}

func (db *StakeDatabase) Open() error {
	db.nodeMtx.Lock()
	defer db.nodeMtx.Unlock()

	// Create a new database to store the accepted stake node data into.
	dbName := DefaultStakeDbName
	var err error
	db.StakeDB, err = database.Open(dbType, dbName, db.params.Net)
	if err != nil {
		log.Infof("Unable to open stake DB (%v). Removing and creating new.", err)
		_ = os.RemoveAll(dbName)
		db.StakeDB, err = database.Create(dbType, dbName, db.params.Net)
		if err != nil {
			// do not return nil interface, but interface of nil DB
			return fmt.Errorf("error creating db: %v", err)
		}
	}

	// Load the best block from stake db
	var bestNodeHeight = int64(-1)
	err = db.StakeDB.View(func(dbTx database.Tx) error {
		v := dbTx.Metadata().Get([]byte("stakechainstate"))
		if v == nil {
			return fmt.Errorf("missing key for chain state data")
		}

		var stakeDBHash chainhash.Hash
		copy(stakeDBHash[:], v[:chainhash.HashSize])
		offset := chainhash.HashSize
		stakeDBHeight := binary.LittleEndian.Uint32(v[offset : offset+4])
		bestNodeHeight = int64(stakeDBHeight)

		var errLocal error
		block, errLocal := db.NodeClient.GetBlock(&stakeDBHash)
		if errLocal != nil {
			return fmt.Errorf("GetBlock failed (%s): %v", stakeDBHash, errLocal)
		}
		header := block.MsgBlock().Header

		db.BestNode, errLocal = stake.LoadBestNode(dbTx, stakeDBHeight,
			stakeDBHash, header, db.params)
		return errLocal
	})
	if err != nil {
		log.Errorf("Error reading from database (%v).  Reinitializing.", err)
		err = db.StakeDB.Update(func(dbTx database.Tx) error {
			var errLocal error
			db.BestNode, errLocal = stake.InitDatabaseState(dbTx, db.params)
			return errLocal
		})
		log.Debug("Created new stake db.")
	} else {
		log.Debug("Opened existing stake db.")
	}

	return err
}
<|endoftext|>


<commit_before>package api

import (
	"code.google.com/p/go.net/websocket"
	"crypto/tls"
	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/state"
	"net"
	"net/http"
)

// srvState represents a single client's connection to the state.
type srvState struct {
	state *state.State
	conn  *websocket.Conn
}

type Server struct {
	state *state.State
}

// Serve serves the given state by accepting requests
// on the given listener, using the given certificate
// and key (in PEM format) for authentication.
func Serve(s *state.State, lis net.Listener, cert, key []byte) error {
	tlsCert, err := tls.X509KeyPair(cert, key)
	if err != nil {
		return err
	}
	lis = tls.NewListener(lis, &tls.Config{
		Certificates: []tls.Certificate{tlsCert},
	})
	return http.Serve(lis, newHandler(s))
}

// newHandler returns an http handler that serves the API
// interface to the given state as a websocket.
func newHandler(s *state.State) http.Handler {
	return websocket.Handler(func(conn *websocket.Conn) {
		srv := &srvState{
			state: s,
			conn:  conn,
		}
		srv.run()
	})
}

type rpcRequest struct {
	Request string // placeholder only
}

type rpcResponse struct {
	Response string // placeholder only
	Error    string
}

func (srv *srvState) run() {
	for {
		var req rpcRequest
		err := websocket.JSON.Receive(srv.conn, &req)
		if err != nil {
			log.Printf("api: error receiving request: %v", err)
			return
		}
		var resp rpcResponse
		// placeholder for executing some arbitrary operation
		// on state.
		m, err := srv.state.Machine(req.Request)
		if err != nil {
			resp.Error = err.Error()
		} else {
			instId, err := m.InstanceId()
			if err != nil {
				resp.Error = err.Error()
			} else {
				resp.Response = string(instId)
			}
		}
		err = websocket.JSON.Send(srv.conn, &resp)
		if err != nil {
			log.Printf("api: error sending reply: %v", err)
			return
		}
	}
}
<commit_msg>go fmt fix<commit_after>package api

import (
	"code.google.com/p/go.net/websocket"
	"crypto/tls"
	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/state"
	"net"
	"net/http"
)

// srvState represents a single client's connection to the state.
type srvState struct {
	state *state.State
	conn  *websocket.Conn
}

type Server struct {
	state *state.State
}

// Serve serves the given state by accepting requests
// on the given listener, using the given certificate
// and key (in PEM format) for authentication.
func Serve(s *state.State, lis net.Listener, cert, key []byte) error {
	tlsCert, err := tls.X509KeyPair(cert, key)
	if err != nil {
		return err
	}
	lis = tls.NewListener(lis, &tls.Config{
		Certificates: []tls.Certificate{tlsCert},
	})
	return http.Serve(lis, newHandler(s))
}

// newHandler returns an http handler that serves the API
// interface to the given state as a websocket.
func newHandler(s *state.State) http.Handler {
	return websocket.Handler(func(conn *websocket.Conn) {
		srv := &srvState{
			state: s,
			conn:  conn,
		}
		srv.run()
	})
}

type rpcRequest struct {
	Request string // placeholder only
}

type rpcResponse struct {
	Response string // placeholder only
	Error    string
}

func (srv *srvState) run() {
	for {
		var req rpcRequest
		err := websocket.JSON.Receive(srv.conn, &req)
		if err != nil {
			log.Printf("api: error receiving request: %v", err)
			return
		}
		var resp rpcResponse
		// placeholder for executing some arbitrary operation
		// on state.
		m, err := srv.state.Machine(req.Request)
		if err != nil {
			resp.Error = err.Error()
		} else {
			instId, err := m.InstanceId()
			if err != nil {
				resp.Error = err.Error()
			} else {
				resp.Response = string(instId)
			}
		}
		err = websocket.JSON.Send(srv.conn, &resp)
		if err != nil {
			log.Printf("api: error sending reply: %v", err)
			return
		}
	}
}
<|endoftext|>


<commit_before>package stathat

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/timehop/golog/log"
)

const (
	logID = "Stathat Batcher"
)

var (
	APIURL = "http://api.stathat.com/ez"

	ErrCouldNotQueueStat    = errors.New("could not queue up stat")
	ErrInvalidFlushInterval = errors.New("flush interval invalid")
)

type Stat struct {
	Stat  string  `json:"stat"`
	Count float64 `json:"count,omitempty"`
	Value float64 `json:"value,omitempty"`
	Time  int64   `json:"t"`
}

type BulkStat struct {
	EzKey string  `json:"ezkey"`
	Data  []*Stat `json:"data"`
}

type Batcher struct {
	Stats         chan Stat
	stop          chan interface{}
	EZKey         string
	flushInterval time.Duration
}

func NewBatcher(ezKey string, d time.Duration) (Batcher, error) {
	if d < 0 {
		return Batcher{}, ErrInvalidFlushInterval
	}

	c := make(chan Stat, 10000)
	st := make(chan interface{})

	return Batcher{EZKey: ezKey, flushInterval: d, stop: st, Stats: c}, nil
}

func (b Batcher) PostEZCount(statName string, count int) error {
	return b.PostEZCountTime(statName, count, time.Now().Unix())
}

func (b Batcher) PostEZCountTime(statName string, count int, timestamp int64) error {
	s := Stat{
		Stat:  statName,
		Count: float64(count),
		Time:  timestamp,
	}

	return b.record(s)
}

func (b Batcher) PostEZValue(statName string, value float64) error {
	return b.PostEZValueTime(statName, value, time.Now().Unix())
}

func (b Batcher) PostEZValueTime(statName string, value float64, timestamp int64) error {
	s := Stat{
		Stat:  statName,
		Value: value,
		Time:  timestamp,
	}

	return b.record(s)
}

func (b Batcher) Stop() error {
	b.stop <- true
	return nil
}

func (b Batcher) record(s Stat) error {
	b.Stats <- s
	return nil
}

func (b Batcher) Start() {
	ss := []*Stat{}
	t := time.Tick(b.flushInterval)

	for {
		select {
		case s := <-b.Stats:
			// Only do any work if EZKey is set
			ss = append(ss, &s)
		case <-t:
			go b.flush(ss)
			ss = []*Stat{}
		case <-b.stop:
			break
		}
	}
}

func (b Batcher) flush(stats []*Stat) {
	if len(stats) == 0 {
		return
	}

	if b.EZKey == "" {
		log.Warn(logID, "Skipping flush. ez key not set.", "stats", len(stats))
		return
	}

	for chunk := range chunks(stats) {
		j, err := json.Marshal(BulkStat{EzKey: b.EZKey, Data: chunk})
		if err != nil {
			log.Warn(logID, "couldn't marshal bulk data", "error", err.Error())
			return
		}

		req, err := http.NewRequest("POST", APIURL, bytes.NewReader(j))
		if err != nil {
			log.Warn(logID, "couldn't make request", "error", err.Error())
			return
		}

		req.Header.Add("Content-Type", "application/json")

		send(req, 2)
	}
}

func chunks(stats []*Stat) chan []*Stat {
	c := make(chan []*Stat)
	go func() {
		chunks := len(stats) / 1000
		for i := 0; i <= chunks; i++ {

			start := i * 1000
			end := start + 1000

			if end > len(stats)-1 {
				end = len(stats)
			}

			c <- stats[start:end]
		}
	}()

	return c
}

func send(req *http.Request, retries int) {
	for i := 0; i < retries; i++ {
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			log.Warn(logID, "error posting data to stathat", "error", err.Error())
			continue
		}

		b, _ := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()

		log.Debug(logID, "Flushed", "status", resp.Status, "resp", string(b))
		break
	}
}
<commit_msg>D: leaks.<commit_after>package stathat

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"time"

	"github.com/timehop/golog/log"
)

const (
	logID = "Stathat Batcher"
)

var (
	APIURL = "http://api.stathat.com/ez"

	ErrCouldNotQueueStat    = errors.New("could not queue up stat")
	ErrInvalidFlushInterval = errors.New("flush interval invalid")
)

type Stat struct {
	Stat  string  `json:"stat"`
	Count float64 `json:"count,omitempty"`
	Value float64 `json:"value,omitempty"`
	Time  int64   `json:"t"`
}

type BulkStat struct {
	EzKey string  `json:"ezkey"`
	Data  []*Stat `json:"data"`
}

type Batcher struct {
	Stats         chan Stat
	stop          chan interface{}
	EZKey         string
	flushInterval time.Duration
}

func NewBatcher(ezKey string, d time.Duration) (Batcher, error) {
	if d < 0 {
		return Batcher{}, ErrInvalidFlushInterval
	}

	c := make(chan Stat, 10000)
	st := make(chan interface{})

	return Batcher{EZKey: ezKey, flushInterval: d, stop: st, Stats: c}, nil
}

func (b Batcher) PostEZCount(statName string, count int) error {
	return b.PostEZCountTime(statName, count, time.Now().Unix())
}

func (b Batcher) PostEZCountTime(statName string, count int, timestamp int64) error {
	s := Stat{
		Stat:  statName,
		Count: float64(count),
		Time:  timestamp,
	}

	return b.record(s)
}

func (b Batcher) PostEZValue(statName string, value float64) error {
	return b.PostEZValueTime(statName, value, time.Now().Unix())
}

func (b Batcher) PostEZValueTime(statName string, value float64, timestamp int64) error {
	s := Stat{
		Stat:  statName,
		Value: value,
		Time:  timestamp,
	}

	return b.record(s)
}

func (b Batcher) Stop() error {
	b.stop <- true
	return nil
}

func (b Batcher) record(s Stat) error {
	b.Stats <- s
	return nil
}

func (b Batcher) Start() {
	ss := []*Stat{}
	t := time.Tick(b.flushInterval)

	for {
		select {
		case s := <-b.Stats:
			// Only do any work if EZKey is set
			ss = append(ss, &s)
		case <-t:
			go b.flush(ss)
			ss = []*Stat{}
		case <-b.stop:
			break
		}
	}
}

func (b Batcher) flush(stats []*Stat) {
	if len(stats) == 0 {
		return
	}

	if b.EZKey == "" {
		log.Warn(logID, "Skipping flush. ez key not set.", "stats", len(stats))
		return
	}

	for chunk := range chunks(stats) {
		j, err := json.Marshal(BulkStat{EzKey: b.EZKey, Data: chunk})
		if err != nil {
			log.Warn(logID, "couldn't marshal bulk data", "error", err.Error())
			return
		}

		req, err := http.NewRequest("POST", APIURL, bytes.NewReader(j))
		if err != nil {
			log.Warn(logID, "couldn't make request", "error", err.Error())
			return
		}

		req.Header.Add("Content-Type", "application/json")

		send(req, 2)
	}
}

func chunks(stats []*Stat) chan []*Stat {
	c := make(chan []*Stat)
	go func() {
		chunks := len(stats) / 1000
		for i := 0; i <= chunks; i++ {

			start := i * 1000
			end := start + 1000

			if end > len(stats)-1 {
				end = len(stats)
			}

			c <- stats[start:end]
		}

		close(c)
	}()

	return c
}

func send(req *http.Request, retries int) {
	for i := 0; i < retries; i++ {
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			log.Warn(logID, "error posting data to stathat", "error", err.Error())
			continue
		}

		b, _ := ioutil.ReadAll(resp.Body)
		defer resp.Body.Close()

		log.Debug(logID, "Flushed", "status", resp.Status, "resp", string(b))
		break
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
	"time"
	"vaddy/args"
	"vaddy/common"
	"vaddy/config"
	"vaddy/crawl"
	"vaddy/httpreq"
	"vaddy/notification"
	"vaddy/scan"
)

const SUCCESS_EXIT int = 0
const ERROR_EXIT int = 1

func init() {
	var showVersion bool
	flag.BoolVar(&showVersion, "version", false, "Show version")
	flag.BoolVar(&showVersion, "v", false, "Show version")
	flag.Parse()

	if showVersion {
		fmt.Printf("%s", config.VERSION)
		os.Exit(SUCCESS_EXIT)
	}
}

func main() {
	fmt.Println("==== Start VAddy Scan (Version " + config.VERSION + ")====")

	scanSetting, err := args.GetArgsFromEnv()
	if err != nil {
		fmt.Println("Missing arguments or system env.")
		fmt.Println(" ENV VADDY_USER: " + scanSetting.User)
		fmt.Println(" ENV VADDY_PROJECT_ID(V2): " + scanSetting.ProjectId)
		fmt.Println(" ENV VADDY_HOST(V1): " + scanSetting.ScanType)
		os.Exit(ERROR_EXIT)
	}

	if scanSetting.CheckNeedToGetCrawlId() {
		fmt.Println("Start to get crawl ID from keyword: " + scanSetting.Crawl)
		scanSetting.CrawlId, err = crawl.GetCrawlId(httpreq.HttpRequestData{}, scanSetting)
		if err != nil {
			fmt.Println(err)
			os.Exit(ERROR_EXIT)
		}
	}

	scanId, err := scan.StartScan(httpreq.HttpRequestData{}, scanSetting)
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
	fmt.Println("Scan Started. ScanID: " + scanId)

	var waitCount int = 0
	for {
		scanResult, err := scan.GetScanResult(httpreq.HttpRequestData{}, scanSetting, scanId)
		if err != nil {
			fmt.Println(err)
			os.Exit(ERROR_EXIT)
		}

		if scanResult.IsScanRunning() {
			common.PrintDots(waitCount)
		}

		if scanResult.IsCanceled() {
			fmt.Println("Scan canceled.")
			fmt.Println("Result URL: " + scanResult.ScanResultUrl)
			os.Exit(ERROR_EXIT)
		}

		if scanResult.IsFinished() {
			fmt.Println(".")
			fqdnOrProjectIdLabel := scanSetting.GetHostOrProjectName()
			fmt.Println(fqdnOrProjectIdLabel)
			fmt.Println("scanId: " + scanId)
			fmt.Println("Result URL: " + scanResult.ScanResultUrl)
			fmt.Println("Crawl ID: " + strconv.Itoa(scanResult.CrawlId))
			fmt.Println("Crawl Label: " + scanResult.CrawlLabel)
			scanSetting.PrintScanTypeSetting()

			if scanResult.IsVulnExist() {
				fmt.Print("Vulnerabilities: ")
				fmt.Println(scanResult.AlertCount)
				fmt.Println("Warning!!!")
				notification.PostSlackVulnerabilitiesWarning(scanResult.AlertCount, fqdnOrProjectIdLabel, scanId, scanResult.ScanResultUrl)
				os.Exit(ERROR_EXIT)
			} else if scanResult.IsIncomplete() {
				fmt.Printf("Notice: Scan was NOT complete (%d%%).\n", scanResult.Complete)
				fmt.Println("No vulnerabilities.")
				notification.PostSlackIncompleteNotice(fqdnOrProjectIdLabel, scanId, scanResult)
				os.Exit(SUCCESS_EXIT)
			} else {
				fmt.Println("Scan Success. No vulnerabilities!")
				os.Exit(SUCCESS_EXIT)
			}
		}

		sleepSec := common.GetSleepSec(waitCount)
		time.Sleep(time.Duration(sleepSec) * time.Second)

		if isTimeOver(waitCount) {
			fmt.Println("Error: time out")
			os.Exit(ERROR_EXIT)
		}
		waitCount++
	}
}

func isTimeOver(waitCount int) bool {
	if waitCount > config.LIMIT_WAIT_COUNT {
		return true
	}
	return false
}
<commit_msg>httpリクエストレスポンスのインスタンスは事前にpkg内の変数にセットする形にしたためメイン関数からの呼び出しも変更<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"strconv"
	"time"
	"vaddy/args"
	"vaddy/common"
	"vaddy/config"
	"vaddy/crawl"
	"vaddy/notification"
	"vaddy/scan"
)

const SUCCESS_EXIT int = 0
const ERROR_EXIT int = 1

func init() {
	var showVersion bool
	flag.BoolVar(&showVersion, "version", false, "Show version")
	flag.BoolVar(&showVersion, "v", false, "Show version")
	flag.Parse()

	if showVersion {
		fmt.Printf("%s", config.VERSION)
		os.Exit(SUCCESS_EXIT)
	}
}

func main() {
	fmt.Println("==== Start VAddy Scan (Version " + config.VERSION + ")====")

	scanSetting, err := args.GetArgsFromEnv()
	if err != nil {
		fmt.Println("Missing arguments or system env.")
		fmt.Println(" ENV VADDY_USER: " + scanSetting.User)
		fmt.Println(" ENV VADDY_PROJECT_ID(V2): " + scanSetting.ProjectId)
		fmt.Println(" ENV VADDY_HOST(V1): " + scanSetting.ScanType)
		os.Exit(ERROR_EXIT)
	}

	if scanSetting.CheckNeedToGetCrawlId() {
		fmt.Println("Start to get crawl ID from keyword: " + scanSetting.Crawl)
		scanSetting.CrawlId, err = crawl.GetCrawlId(scanSetting)
		if err != nil {
			fmt.Println(err)
			os.Exit(ERROR_EXIT)
		}
	}

	scanId, err := scan.StartScan(scanSetting)
	if err != nil {
		fmt.Println(err)
		os.Exit(ERROR_EXIT)
	}
	fmt.Println("Scan Started. ScanID: " + scanId)

	var waitCount int = 0
	for {
		scanResult, err := scan.GetScanResult(scanSetting, scanId)
		if err != nil {
			fmt.Println(err)
			os.Exit(ERROR_EXIT)
		}

		if scanResult.IsScanRunning() {
			common.PrintDots(waitCount)
		}

		if scanResult.IsCanceled() {
			fmt.Println("Scan canceled.")
			fmt.Println("Result URL: " + scanResult.ScanResultUrl)
			os.Exit(ERROR_EXIT)
		}

		if scanResult.IsFinished() {
			fmt.Println(".")
			fqdnOrProjectIdLabel := scanSetting.GetHostOrProjectName()
			fmt.Println(fqdnOrProjectIdLabel)
			fmt.Println("scanId: " + scanId)
			fmt.Println("Result URL: " + scanResult.ScanResultUrl)
			fmt.Println("Crawl ID: " + strconv.Itoa(scanResult.CrawlId))
			fmt.Println("Crawl Label: " + scanResult.CrawlLabel)
			scanSetting.PrintScanTypeSetting()

			if scanResult.IsVulnExist() {
				fmt.Print("Vulnerabilities: ")
				fmt.Println(scanResult.AlertCount)
				fmt.Println("Warning!!!")
				notification.PostSlackVulnerabilitiesWarning(scanResult.AlertCount, fqdnOrProjectIdLabel, scanId, scanResult.ScanResultUrl)
				os.Exit(ERROR_EXIT)
			} else if scanResult.IsIncomplete() {
				fmt.Printf("Notice: Scan was NOT complete (%d%%).\n", scanResult.Complete)
				fmt.Println("No vulnerabilities.")
				notification.PostSlackIncompleteNotice(fqdnOrProjectIdLabel, scanId, scanResult)
				os.Exit(SUCCESS_EXIT)
			} else {
				fmt.Println("Scan Success. No vulnerabilities!")
				os.Exit(SUCCESS_EXIT)
			}
		}

		sleepSec := common.GetSleepSec(waitCount)
		time.Sleep(time.Duration(sleepSec) * time.Second)

		if isTimeOver(waitCount) {
			fmt.Println("Error: time out")
			os.Exit(ERROR_EXIT)
		}
		waitCount++
	}
}

func isTimeOver(waitCount int) bool {
	if waitCount > config.LIMIT_WAIT_COUNT {
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package step

import (
	"errors"
	"fmt"
	"github.com/LastCallMedia/vagabond/config"
	"os/exec"
)

var ExportsTemplate = `{{.UsersDir}} {{.DockerDaemonIp}} -alldirs -mapall=501:20
{{.DataDir}} {{.DockerDaemonIp}} -alldirs -maproot=0`

var NfsServerStep = ConfigStep{
	Name: "nfs server",
	NeedsRun: func(envt *config.Environment) bool {
		exports, err := doTemplateAppend(ExportsTemplate, envt, "/etc/exports")
		if err != nil {
			return true
		}
		matches, err := checkIfFileMatches("/etc/exports", exports)

		return err != nil || !matches
	},
	Run: func(envt *config.Environment) (err error) {
		fmt.Println("Editing /etc/profile... sudo privileges may be required")
		out, err := exec.Command("sudo", "touch", "/etc/exports").CombinedOutput()
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to create/update exports file: %s", string(out)))
		}

		exports, err := doTemplateAppend(ExportsTemplate, envt, "/etc/exports")
		if err != nil {
			return err
		}

		cmd := exec.Command("sudo", "tee", "/etc/exports")
		pipeInputToCmd(cmd, exports)
		out, err = cmd.CombinedOutput()
		if err != nil {
			return errors.New(string(out))
		}

		cmd = exec.Command("sudo", "nfsd", "restart")
		out, err = cmd.CombinedOutput()
		if err != nil {
			return errors.New(string(out))
		}
		return
	},
}
<commit_msg>Fix sudo warning message on editing exports<commit_after>package step

import (
	"errors"
	"fmt"
	"github.com/LastCallMedia/vagabond/config"
	"os/exec"
)

var ExportsTemplate = `{{.UsersDir}} {{.DockerDaemonIp}} -alldirs -mapall=501:20
{{.DataDir}} {{.DockerDaemonIp}} -alldirs -maproot=0`

var NfsServerStep = ConfigStep{
	Name: "nfs server",
	NeedsRun: func(envt *config.Environment) bool {
		exports, err := doTemplateAppend(ExportsTemplate, envt, "/etc/exports")
		if err != nil {
			return true
		}
		matches, err := checkIfFileMatches("/etc/exports", exports)

		return err != nil || !matches
	},
	Run: func(envt *config.Environment) (err error) {
		fmt.Println("Editing /etc/exports... sudo privileges may be required")
		out, err := exec.Command("sudo", "touch", "/etc/exports").CombinedOutput()
		if err != nil {
			return errors.New(fmt.Sprintf("Failed to create/update exports file: %s", string(out)))
		}

		exports, err := doTemplateAppend(ExportsTemplate, envt, "/etc/exports")
		if err != nil {
			return err
		}

		cmd := exec.Command("sudo", "tee", "/etc/exports")
		pipeInputToCmd(cmd, exports)
		out, err = cmd.CombinedOutput()
		if err != nil {
			return errors.New(string(out))
		}

		cmd = exec.Command("sudo", "nfsd", "restart")
		out, err = cmd.CombinedOutput()
		if err != nil {
			return errors.New(string(out))
		}
		return
	},
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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 ksr

import (
	"fmt"
	"github.com/contiv/vpp/plugins/ksr/model/ksrapi"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/onsi/gomega"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	dto "github.com/prometheus/client_model/go"

	"testing"
)

const (
	newRegistryTestError = "new Registry Test Error"
	newGaugeVecTestError = "new Gauge Vector Test Error"
	testReflector        = "testReflector"
)

type mockPrometheus struct {
	statsPath        string
	newRegistryError error
	registerError    error
}

type StatsCollectorTestVars struct {
	mockPrometheus *mockPrometheus
	statsCollector *StatsCollector
}

type mockGauge struct {
	value float64
}

type mockGaugeVec struct {
	gauges map[string]mockGauge
}

var scTestVars StatsCollectorTestVars

func TestStatsCollector(t *testing.T) {
	gomega.RegisterTestingT(t)

	flavorLocal := &local.FlavorLocal{}
	flavorLocal.Inject()

	scTestVars.mockPrometheus = &mockPrometheus{
		newRegistryError: nil,
		registerError:    nil,
	}

	scTestVars.statsCollector = &StatsCollector{
		Log:          flavorLocal.LoggerFor("stats-collector"),
		serviceLabel: "StatsCollectorTest",
		Prometheus:   scTestVars.mockPrometheus,
	}

	scTestVars.mockPrometheus.injectNewRegistryFuncError(fmt.Errorf("%s", newRegistryTestError))
	err := scTestVars.statsCollector.Init()
	gomega.Expect(err).To(gomega.MatchError(newRegistryTestError))

	scTestVars.mockPrometheus.injectNewRegistryFuncError(nil)
	scTestVars.mockPrometheus.injectRegisterFuncError(fmt.Errorf("%s", newGaugeVecTestError))
	err = scTestVars.statsCollector.Init()
	gomega.Expect(err).To(gomega.MatchError(newGaugeVecTestError))

	scTestVars.mockPrometheus.injectRegisterFuncError(nil)
	err = scTestVars.statsCollector.Init()
	gomega.Expect(err).To(gomega.BeNil())
	gomega.Expect(len(scTestVars.statsCollector.gaugeVecs)).To(gomega.Equal(9))

	t.Run("testAddReflector", testAddReflector)
	t.Run("testUpdatePrometheusStats", testUpdatePrometheusStats)
}

func testAddReflector(t *testing.T) {
	scTestVars.statsCollector.addReflector(testReflector)
	gomega.Expect(len(scTestVars.statsCollector.metrics)).To(gomega.Equal(1))
	gomega.Expect(len(scTestVars.statsCollector.metrics[testReflector].gauges)).To(gomega.Equal(9))
}

func testUpdatePrometheusStats(t *testing.T) {
	// Test data
	stats := &ksrapi.KsrStats{
		Adds:      1,
		Deletes:   2,
		Updates:   3,
		AddErrors: 4,
		DelErrors: 5,
		UpdErrors: 6,
		ArgErrors: 7,
		Resyncs:   8,
		ResErrors: 9,
	}

	// Replaces Prometheus gauges in Stats Collector with mocks
	gauges := scTestVars.statsCollector.metrics[testReflector].gauges

	gauges[addsMetric] = &mockGauge{value: float64(0)}
	gauges[deletesMetric] = &mockGauge{value: float64(0)}
	gauges[updatesMetric] = &mockGauge{value: float64(0)}
	gauges[addErrMetric] = &mockGauge{value: float64(0)}
	gauges[deleteErrMetric] = &mockGauge{value: float64(0)}
	gauges[updateErrMetric] = &mockGauge{value: float64(0)}
	gauges[argErrMetric] = &mockGauge{value: float64(0)}
	gauges[argErrMetric] = &mockGauge{value: float64(0)}
	gauges[resyncsMetric] = &mockGauge{value: float64(0)}
	gauges[resyncErrMetric] = &mockGauge{value: float64(0)}

	scTestVars.statsCollector.updatePrometheusStats(testReflector, stats)

	// Check that correct values have been written into the gauges.
	gomega.Expect((gauges[addsMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Adds)))
	gomega.Expect((gauges[deletesMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Deletes)))
	gomega.Expect((gauges[updatesMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Updates)))
	gomega.Expect((gauges[addErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.AddErrors)))
	gomega.Expect((gauges[deleteErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.DelErrors)))
	gomega.Expect((gauges[updateErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.UpdErrors)))
	gomega.Expect((gauges[argErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.ArgErrors)))
	gomega.Expect((gauges[resyncsMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Resyncs)))
	gomega.Expect((gauges[resyncErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.ResErrors)))
}

// NewRegistry creates new registry exposed at defined URL path (must begin
// with '/' character), path is used to reference registry while adding new
// metrics into registry, opts adjust the behavior of exposed registry. Must
// be called before AfterInit phase of the Prometheus plugin. An attempt to
// create  a registry with path that is already used
// by different registry returns an error.
func (mp *mockPrometheus) NewRegistry(path string, opts promhttp.HandlerOpts) error {
	mp.statsPath = path
	return mp.newRegistryError
}

// Register registers prometheus metric (e.g.: created by prometheus.NewGaugeVec,
// prometheus.NewHistogram,...)  to a specified registry
func (mp *mockPrometheus) Register(registryPath string, collector prometheus.Collector) error {
	return mp.registerError
}

// Unregister unregisters the given metric. The function returns whether a
// Collector was unregistered.
func (mp *mockPrometheus) Unregister(registryPath string, collector prometheus.Collector) bool {
	return false
}

// RegisterGauge registers custom gauge with specific valueFunc to report
// status when invoked. RegistryPath identifies the registry. The aim of this
// method is to simply common use case - adding Gauge with value func.
func (mp *mockPrometheus) RegisterGaugeFunc(registryPath string, namespace string, subsystem string,
	name string, help string, labels prometheus.Labels, valueFunc func() float64) error {
	return nil
}

func (mp *mockPrometheus) injectNewRegistryFuncError(err error) {
	mp.newRegistryError = err
}

func (mp *mockPrometheus) injectRegisterFuncError(err error) {
	mp.registerError = err
}

// Desc returns the descriptor for the Metric. This method idempotently
// returns the same descriptor throughout the lifetime of the
// Metric. The returned descriptor is immutable by contract. A Metric
// unable to describe itself must return an invalid descriptor (created
// with NewInvalidDesc).
func (mg *mockGauge) Desc() *prometheus.Desc {
	return nil
}

// Write encodes the Metric into a "Metric" Protocol Buffer data
// transmission object.
//
// Metric implementations must observe concurrency safety as reads of
// this metric may occur at any time, and any blocking occurs at the
// expense of total performance of rendering all registered
// metrics. Ideally, Metric implementations should support concurrent
// readers.
//
// While populating dto.Metric, it is the responsibility of the
// implementation to ensure validity of the Metric protobuf (like valid
// UTF-8 strings or syntactically valid metric and label names). It is
// recommended to sort labels lexicographically. (Implementers may find
// LabelPairSorter useful for that.) Callers of Write should still make
// sure of sorting if they depend on it.
func (mg *mockGauge) Write(*dto.Metric) error {
	return nil
}

// Describe sends the super-set of all possible descriptors of metrics
// collected by this Collector to the provided channel and returns once
// the last descriptor has been sent. The sent descriptors fulfill the
// consistency and uniqueness requirements described in the Desc
// documentation. (It is valid if one and the same Collector sends
// duplicate descriptors. Those duplicates are simply ignored. However,
// two different Collectors must not send duplicate descriptors.) This
// method idempotently sends the same descriptors throughout the
// lifetime of the Collector. If a Collector encounters an error while
// executing this method, it must send an invalid descriptor (created
// with NewInvalidDesc) to signal the error to the registry.
func (mg *mockGauge) Describe(chan<- *prometheus.Desc) {

}

// Collect is called by the Prometheus registry when collecting
// metrics. The implementation sends each collected metric via the
// provided channel and returns once the last metric has been sent. The
// descriptor of each sent metric is one of those returned by
// Describe. Returned metrics that share the same descriptor must differ
// in their variable label values. This method may be called
// concurrently and must therefore be implemented in a concurrency safe
// way. Blocking occurs at the expense of total performance of rendering
// all registered metrics. Ideally, Collector implementations support
// concurrent readers.
func (mg *mockGauge) Collect(chan<- prometheus.Metric) {

}

// Set sets the Gauge to an arbitrary value.
func (mg *mockGauge) Set(value float64) {
	mg.value = value
}

// Inc increments the Gauge by 1.
func (mg *mockGauge) Inc() {

}

// Dec decrements the Gauge by 1.
func (mg *mockGauge) Dec() {

}

// Add adds the given value to the Gauge. (The value can be
// negative, resulting in a decrease of the Gauge.)
func (mg *mockGauge) Add(float64) {

}

// Sub subtracts the given value from the Gauge. (The value can be
// negative, resulting in an increase of the Gauge.)
func (mg *mockGauge) Sub(float64) {

}
<commit_msg>Clean up the Stats Collector unit tests<commit_after>// Copyright (c) 2018 Cisco and/or its affiliates.
//
// 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 ksr

import (
	"fmt"
	"github.com/contiv/vpp/plugins/ksr/model/ksrapi"
	"github.com/ligato/cn-infra/flavors/local"
	"github.com/onsi/gomega"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	dto "github.com/prometheus/client_model/go"

	"testing"
)

const (
	newRegistryTestError = "new Registry Test Error"
	newGaugeVecTestError = "new Gauge Vector Test Error"
	testReflector        = "testReflector"
)

// mockPrometheus is a mock implementation of the main Prometheus registry
type mockPrometheus struct {
	statsPath        string
	newRegistryError error
	registerError    error
}

// mockGauge is a mock implementation of the Prometheus Gauge
type mockGauge struct {
	value float64
}

type StatsCollectorTestVars struct {
	mockPrometheus *mockPrometheus
	statsCollector *StatsCollector
}

var scTestVars StatsCollectorTestVars

func TestStatsCollector(t *testing.T) {
	gomega.RegisterTestingT(t)

	flavorLocal := &local.FlavorLocal{}
	flavorLocal.Inject()

	scTestVars.mockPrometheus = &mockPrometheus{
		newRegistryError: nil,
		registerError:    nil,
	}

	scTestVars.statsCollector = &StatsCollector{
		Log:          flavorLocal.LoggerFor("stats-collector"),
		serviceLabel: "StatsCollectorTest",
		Prometheus:   scTestVars.mockPrometheus,
	}

	// Check proper handling of registration errors
	scTestVars.mockPrometheus.injectNewRegistryFuncError(fmt.Errorf("%s", newRegistryTestError))
	err := scTestVars.statsCollector.Init()
	gomega.Expect(err).To(gomega.MatchError(newRegistryTestError))

	// Check proper handling of Gauge Vector creation errors
	scTestVars.mockPrometheus.injectNewRegistryFuncError(nil)
	scTestVars.mockPrometheus.injectRegisterFuncError(fmt.Errorf("%s", newGaugeVecTestError))
	err = scTestVars.statsCollector.Init()
	gomega.Expect(err).To(gomega.MatchError(newGaugeVecTestError))

	// Check the "sunny path" initialization
	scTestVars.mockPrometheus.injectRegisterFuncError(nil)
	err = scTestVars.statsCollector.Init()
	gomega.Expect(err).To(gomega.BeNil())
	gomega.Expect(len(scTestVars.statsCollector.gaugeVecs)).To(gomega.Equal(9))

	t.Run("testAddReflector", testAddReflector)
	t.Run("testUpdatePrometheusStats", testUpdatePrometheusStats)
}

func testAddReflector(t *testing.T) {
	scTestVars.statsCollector.addReflector(testReflector)

	gomega.Expect(len(scTestVars.statsCollector.metrics)).To(gomega.Equal(1))
	gomega.Expect(len(scTestVars.statsCollector.metrics[testReflector].gauges)).To(gomega.Equal(9))
}

func testUpdatePrometheusStats(t *testing.T) {
	// Test data
	stats := &ksrapi.KsrStats{
		Adds:      1,
		Deletes:   2,
		Updates:   3,
		AddErrors: 4,
		DelErrors: 5,
		UpdErrors: 6,
		ArgErrors: 7,
		Resyncs:   8,
		ResErrors: 9,
	}

	// Replaces Prometheus gauges in Stats Collector with mocks
	gauges := scTestVars.statsCollector.metrics[testReflector].gauges

	gauges[addsMetric] = &mockGauge{value: float64(0)}
	gauges[deletesMetric] = &mockGauge{value: float64(0)}
	gauges[updatesMetric] = &mockGauge{value: float64(0)}
	gauges[addErrMetric] = &mockGauge{value: float64(0)}
	gauges[deleteErrMetric] = &mockGauge{value: float64(0)}
	gauges[updateErrMetric] = &mockGauge{value: float64(0)}
	gauges[argErrMetric] = &mockGauge{value: float64(0)}
	gauges[argErrMetric] = &mockGauge{value: float64(0)}
	gauges[resyncsMetric] = &mockGauge{value: float64(0)}
	gauges[resyncErrMetric] = &mockGauge{value: float64(0)}

	scTestVars.statsCollector.updatePrometheusStats(testReflector, stats)

	// Check that correct values have been written into the gauges.
	gomega.Expect((gauges[addsMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Adds)))
	gomega.Expect((gauges[deletesMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Deletes)))
	gomega.Expect((gauges[updatesMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Updates)))
	gomega.Expect((gauges[addErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.AddErrors)))
	gomega.Expect((gauges[deleteErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.DelErrors)))
	gomega.Expect((gauges[updateErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.UpdErrors)))
	gomega.Expect((gauges[argErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.ArgErrors)))
	gomega.Expect((gauges[resyncsMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.Resyncs)))
	gomega.Expect((gauges[resyncErrMetric].(*mockGauge)).value).To(gomega.Equal(float64(stats.ResErrors)))
}

// NewRegistry creates new registry exposed at defined URL path (must begin
// with '/' character), path is used to reference registry while adding new
// metrics into registry, opts adjust the behavior of exposed registry. Must
// be called before AfterInit phase of the Prometheus plugin. An attempt to
// create  a registry with path that is already used
// by different registry returns an error.
func (mp *mockPrometheus) NewRegistry(path string, opts promhttp.HandlerOpts) error {
	mp.statsPath = path
	return mp.newRegistryError
}

// Register registers prometheus metric (e.g.: created by prometheus.NewGaugeVec,
// prometheus.NewHistogram,...)  to a specified registry
func (mp *mockPrometheus) Register(registryPath string, collector prometheus.Collector) error {
	return mp.registerError
}

// Unregister unregisters the given metric. The function returns whether a
// Collector was unregistered.
func (mp *mockPrometheus) Unregister(registryPath string, collector prometheus.Collector) bool {
	return false
}

// RegisterGauge registers custom gauge with specific valueFunc to report
// status when invoked. RegistryPath identifies the registry. The aim of this
// method is to simply common use case - adding Gauge with value func.
func (mp *mockPrometheus) RegisterGaugeFunc(registryPath string, namespace string, subsystem string,
	name string, help string, labels prometheus.Labels, valueFunc func() float64) error {
	return nil
}

func (mp *mockPrometheus) injectNewRegistryFuncError(err error) {
	mp.newRegistryError = err
}

func (mp *mockPrometheus) injectRegisterFuncError(err error) {
	mp.registerError = err
}

// Desc returns the descriptor for the Metric. This method idempotently
// returns the same descriptor throughout the lifetime of the
// Metric. The returned descriptor is immutable by contract. A Metric
// unable to describe itself must return an invalid descriptor (created
// with NewInvalidDesc).
func (mg *mockGauge) Desc() *prometheus.Desc {
	return nil
}

// Write encodes the Metric into a "Metric" Protocol Buffer data
// transmission object.
//
// Metric implementations must observe concurrency safety as reads of
// this metric may occur at any time, and any blocking occurs at the
// expense of total performance of rendering all registered
// metrics. Ideally, Metric implementations should support concurrent
// readers.
//
// While populating dto.Metric, it is the responsibility of the
// implementation to ensure validity of the Metric protobuf (like valid
// UTF-8 strings or syntactically valid metric and label names). It is
// recommended to sort labels lexicographically. (Implementers may find
// LabelPairSorter useful for that.) Callers of Write should still make
// sure of sorting if they depend on it.
func (mg *mockGauge) Write(*dto.Metric) error {
	return nil
}

// Describe sends the super-set of all possible descriptors of metrics
// collected by this Collector to the provided channel and returns once
// the last descriptor has been sent. The sent descriptors fulfill the
// consistency and uniqueness requirements described in the Desc
// documentation. (It is valid if one and the same Collector sends
// duplicate descriptors. Those duplicates are simply ignored. However,
// two different Collectors must not send duplicate descriptors.) This
// method idempotently sends the same descriptors throughout the
// lifetime of the Collector. If a Collector encounters an error while
// executing this method, it must send an invalid descriptor (created
// with NewInvalidDesc) to signal the error to the registry.
func (mg *mockGauge) Describe(chan<- *prometheus.Desc) {

}

// Collect is called by the Prometheus registry when collecting
// metrics. The implementation sends each collected metric via the
// provided channel and returns once the last metric has been sent. The
// descriptor of each sent metric is one of those returned by
// Describe. Returned metrics that share the same descriptor must differ
// in their variable label values. This method may be called
// concurrently and must therefore be implemented in a concurrency safe
// way. Blocking occurs at the expense of total performance of rendering
// all registered metrics. Ideally, Collector implementations support
// concurrent readers.
func (mg *mockGauge) Collect(chan<- prometheus.Metric) {

}

// Set sets the Gauge to an arbitrary value.
func (mg *mockGauge) Set(value float64) {
	mg.value = value
}

// Inc increments the Gauge by 1.
func (mg *mockGauge) Inc() {

}

// Dec decrements the Gauge by 1.
func (mg *mockGauge) Dec() {

}

// Add adds the given value to the Gauge. (The value can be
// negative, resulting in a decrease of the Gauge.)
func (mg *mockGauge) Add(float64) {

}

// Sub subtracts the given value from the Gauge. (The value can be
// negative, resulting in an increase of the Gauge.)
func (mg *mockGauge) Sub(float64) {

}
<|endoftext|>


<commit_before>package stringset_test

import (
	"fmt"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/jamesjoshuahill/stringset"
)

var _ = Describe("StringSet", func() {
	var set stringset.StringSet
	var other stringset.StringSet

	Context("when is has no members", func() {
		BeforeEach(func() {
			set = stringset.New()
		})

		AfterEach(func() {
			By("not changing the set")
			Expect(set).To(Equal(stringset.New()))
		})

		It("does not contain a member", func() {
			Expect(set.Contains("monkeys")).To(BeFalse())
		})

		It("lists no members", func() {
			Expect(set.Members()).To(BeEmpty())
		})

		It("prints neatly", func() {
			Expect(fmt.Sprintf("%v", set)).To(Equal(`{}`))
		})

		It("is empty", func() {
			Expect(set.Empty()).To(BeTrue())
		})

		It("has an order of 0", func() {
			Expect(set.Order()).To(Equal(0))
		})

		It("is a subset of itself", func() {
			Expect(set.IsSubset(set)).To(BeTrue())
		})

		It("is not a proper subset of itself", func() {
			Expect(set.IsProperSubset(set)).To(BeFalse())
		})

		It("is a superset of itself", func() {
			Expect(set.IsSuperset(set)).To(BeTrue())
		})
	})

	Context("when it has one member", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys")
		})

		AfterEach(func() {
			By("not changing the set")
			Expect(set).To(Equal(stringset.New("monkeys")))
		})

		It("contains the member", func() {
			Expect(set.Contains("monkeys")).To(BeTrue())
		})

		It("does not contain another member", func() {
			Expect(set.Contains("bananas")).To(BeFalse())
		})

		It("prints neatly", func() {
			Expect(fmt.Sprintf("%v", set)).To(Equal(`{monkeys}`))
		})

		It("is not empty", func() {
			Expect(set.Empty()).To(BeFalse())
		})

		It("has an order of one", func() {
			Expect(set.Order()).To(Equal(1))
		})

		It("is a subset of itself", func() {
			Expect(set.IsSubset(set)).To(BeTrue())
		})

		It("is not a proper subset of itself", func() {
			Expect(set.IsProperSubset(set)).To(BeFalse())
		})

		It("is a superset of itself", func() {
			Expect(set.IsSuperset(set)).To(BeTrue())
		})
	})

	Context("when it has some members", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas", "trees")
		})

		AfterEach(func() {
			By("not changing the set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("lists all members", func() {
			Expect(set.Members()).To(ContainElement("monkeys"))
			Expect(set.Members()).To(ContainElement("bananas"))
			Expect(set.Members()).To(ContainElement("trees"))
		})

		It("prints neatly", func() {
			Expect(fmt.Sprintf("%v", set)).To(Or(
				Equal(`{bananas monkeys trees}`),
				Equal(`{bananas trees monkeys}`),
				Equal(`{monkeys bananas trees}`),
				Equal(`{monkeys trees bananas}`),
				Equal(`{trees bananas monkeys}`),
				Equal(`{trees monkeys bananas}`),
			))
		})

		It("is not empty", func() {
			Expect(set.Empty()).To(BeFalse())
		})

		It("has an order of three", func() {
			Expect(set.Order()).To(Equal(3))
		})

		It("is a subset of itself", func() {
			Expect(set.IsSubset(set)).To(BeTrue())
		})

		It("is not a proper subset of itself", func() {
			Expect(set.IsProperSubset(set)).To(BeFalse())
		})

		It("is a superset of itself", func() {
			Expect(set.IsSuperset(set)).To(BeTrue())
		})
	})

	Context("when empty and the other set has a member", func() {
		BeforeEach(func() {
			set = stringset.New()
			other = stringset.New("trees")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New()))
			Expect(other).To(Equal(stringset.New("trees")))
		})

		It("subtracts nothing", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New()))
		})

		It("has no members in common", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New()))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(other))
		})

		It("includes the member in the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(other))
		})

		It("is a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeTrue())
		})

		It("is a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeTrue())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})

	Context("when it has members and the other set is a subset", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas")
			other = stringset.New("bananas")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas")))
			Expect(other).To(Equal(stringset.New("bananas")))
		})

		It("subtracts the member in common", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New("monkeys")))
		})

		It("intersects", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("bananas")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("subtracts the member in common from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("monkeys")))
		})

		It("is not a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeFalse())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeTrue())
		})
	})

	Context("when it has members and is a subset of the other set", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys")
			other = stringset.New("monkeys", "bananas")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys")))
			Expect(other).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("subtracts all members", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New()))
		})

		It("intersects", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("monkeys")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("subtracts the member in common from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("bananas")))
		})

		It("is a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeTrue())
		})

		It("is a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeTrue())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})

	Context("when it has members and the other set intersects", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas")
			other = stringset.New("bananas", "trees")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas")))
			Expect(other).To(Equal(stringset.New("bananas", "trees")))
		})

		It("subtracts the member in common", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New("monkeys")))
		})

		It("intersects", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("bananas")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("subtracts the member in common from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("monkeys", "trees")))
		})

		It("is not a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeFalse())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})

	Context("when it has members and the other set does not intersect", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas")
			other = stringset.New("trees", "sunshine")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas")))
			Expect(other).To(Equal(stringset.New("trees", "sunshine")))
		})

		It("does not subtract any members", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("does not intersect", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New()))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas", "trees", "sunshine")))
		})

		It("includes all members in the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("monkeys", "bananas", "trees", "sunshine")))
		})

		It("is not a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeFalse())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})
})
<commit_msg>Tests functions on two equal sets<commit_after>package stringset_test

import (
	"fmt"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/jamesjoshuahill/stringset"
)

var _ = Describe("StringSet", func() {
	var set stringset.StringSet
	var other stringset.StringSet

	Context("when is has no members", func() {
		BeforeEach(func() {
			set = stringset.New()
		})

		AfterEach(func() {
			By("not changing the set")
			Expect(set).To(Equal(stringset.New()))
		})

		It("does not contain a member", func() {
			Expect(set.Contains("monkeys")).To(BeFalse())
		})

		It("lists no members", func() {
			Expect(set.Members()).To(BeEmpty())
		})

		It("prints neatly", func() {
			Expect(fmt.Sprintf("%v", set)).To(Equal(`{}`))
		})

		It("is empty", func() {
			Expect(set.Empty()).To(BeTrue())
		})

		It("has an order of 0", func() {
			Expect(set.Order()).To(Equal(0))
		})

		It("is a subset of itself", func() {
			Expect(set.IsSubset(set)).To(BeTrue())
		})

		It("is not a proper subset of itself", func() {
			Expect(set.IsProperSubset(set)).To(BeFalse())
		})

		It("is a superset of itself", func() {
			Expect(set.IsSuperset(set)).To(BeTrue())
		})
	})

	Context("when it has one member", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys")
		})

		AfterEach(func() {
			By("not changing the set")
			Expect(set).To(Equal(stringset.New("monkeys")))
		})

		It("contains the member", func() {
			Expect(set.Contains("monkeys")).To(BeTrue())
		})

		It("does not contain another member", func() {
			Expect(set.Contains("bananas")).To(BeFalse())
		})

		It("prints neatly", func() {
			Expect(fmt.Sprintf("%v", set)).To(Equal(`{monkeys}`))
		})

		It("is not empty", func() {
			Expect(set.Empty()).To(BeFalse())
		})

		It("has an order of one", func() {
			Expect(set.Order()).To(Equal(1))
		})

		It("is a subset of itself", func() {
			Expect(set.IsSubset(set)).To(BeTrue())
		})

		It("is not a proper subset of itself", func() {
			Expect(set.IsProperSubset(set)).To(BeFalse())
		})

		It("is a superset of itself", func() {
			Expect(set.IsSuperset(set)).To(BeTrue())
		})
	})

	Context("when it has some members", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas", "trees")
		})

		AfterEach(func() {
			By("not changing the set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("lists all members", func() {
			Expect(set.Members()).To(ContainElement("monkeys"))
			Expect(set.Members()).To(ContainElement("bananas"))
			Expect(set.Members()).To(ContainElement("trees"))
		})

		It("prints neatly", func() {
			Expect(fmt.Sprintf("%v", set)).To(Or(
				Equal(`{bananas monkeys trees}`),
				Equal(`{bananas trees monkeys}`),
				Equal(`{monkeys bananas trees}`),
				Equal(`{monkeys trees bananas}`),
				Equal(`{trees bananas monkeys}`),
				Equal(`{trees monkeys bananas}`),
			))
		})

		It("is not empty", func() {
			Expect(set.Empty()).To(BeFalse())
		})

		It("has an order of three", func() {
			Expect(set.Order()).To(Equal(3))
		})

		It("is a subset of itself", func() {
			Expect(set.IsSubset(set)).To(BeTrue())
		})

		It("is not a proper subset of itself", func() {
			Expect(set.IsProperSubset(set)).To(BeFalse())
		})

		It("is a superset of itself", func() {
			Expect(set.IsSuperset(set)).To(BeTrue())
		})
	})

	Context("when empty and the other set has a member", func() {
		BeforeEach(func() {
			set = stringset.New()
			other = stringset.New("trees")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New()))
			Expect(other).To(Equal(stringset.New("trees")))
		})

		It("subtracts nothing", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New()))
		})

		It("has no members in common", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New()))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(other))
		})

		It("includes the member in the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(other))
		})

		It("is a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeTrue())
		})

		It("is a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeTrue())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})

	Context("when it has members and the other set is a subset", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas")
			other = stringset.New("bananas")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas")))
			Expect(other).To(Equal(stringset.New("bananas")))
		})

		It("subtracts the member in common", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New("monkeys")))
		})

		It("intersects", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("bananas")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("subtracts the member in common from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("monkeys")))
		})

		It("is not a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeFalse())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeTrue())
		})
	})

	Context("when it has members and is a subset of the other set", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys")
			other = stringset.New("monkeys", "bananas")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys")))
			Expect(other).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("subtracts all members", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New()))
		})

		It("intersects", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("monkeys")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("subtracts the member in common from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("bananas")))
		})

		It("is a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeTrue())
		})

		It("is a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeTrue())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})

	Context("when it has members and equals the other set", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas", "trees")
			other = stringset.New("monkeys", "bananas", "trees")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas", "trees")))
			Expect(other).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("subtracts all members", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New()))
		})

		It("intersects all members", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("subtracts all members from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New()))
		})

		It("is a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeTrue())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeTrue())
		})
	})

	Context("when it has members and the other set intersects", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas")
			other = stringset.New("bananas", "trees")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas")))
			Expect(other).To(Equal(stringset.New("bananas", "trees")))
		})

		It("subtracts the member in common", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New("monkeys")))
		})

		It("intersects", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New("bananas")))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas", "trees")))
		})

		It("subtracts the member in common from the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("monkeys", "trees")))
		})

		It("is not a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeFalse())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})

	Context("when it has members and the other set does not intersect", func() {
		BeforeEach(func() {
			set = stringset.New("monkeys", "bananas")
			other = stringset.New("trees", "sunshine")
		})

		AfterEach(func() {
			By("not changing either set")
			Expect(set).To(Equal(stringset.New("monkeys", "bananas")))
			Expect(other).To(Equal(stringset.New("trees", "sunshine")))
		})

		It("does not subtract any members", func() {
			Expect(set.Subtract(other)).To(Equal(stringset.New("monkeys", "bananas")))
		})

		It("does not intersect", func() {
			Expect(set.Intersection(other)).To(Equal(stringset.New()))
		})

		It("includes all members in the union", func() {
			Expect(set.Union(other)).To(Equal(stringset.New("monkeys", "bananas", "trees", "sunshine")))
		})

		It("includes all members in the symmetric difference", func() {
			Expect(set.SymmetricDifference(other)).To(Equal(stringset.New("monkeys", "bananas", "trees", "sunshine")))
		})

		It("is not a subset of the other", func() {
			Expect(set.IsSubset(other)).To(BeFalse())
		})

		It("is not a proper subset the other", func() {
			Expect(set.IsProperSubset(other)).To(BeFalse())
		})

		It("is not a superset of the other", func() {
			Expect(set.IsSuperset(other)).To(BeFalse())
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"log"
	"os"
)

func main() {
	os.Exit(run(NewApp()))
}

const exitOk = iota

func run(app *App) int {
	return exitOk
}

// App represent application and
// holds configuration.
type App struct {
	osargs []string
	stdout io.Writer
	logger *log.Logger
}

// NewApp create application.
func NewApp() *App {
	return &App{
		osargs: os.Args,
		stdout: os.Stdout,
		logger: log.New(os.Stderr, "", 0)}
}

// Log prints to logger like log.Print.
func (a *App) Log(v ...interface{}) {
	a.logger.Print(v...)
}

// Logf prints to logger like log.Printf.
func (a *App) Logf(format string, v ...interface{}) {
	a.logger.Printf(format, v...)
}

// Logln prints to logger like log.Println.
func (a *App) Logln(v ...interface{}) {
	a.logger.Println(v...)
}

func (a *App) write(w io.Writer, v ...interface{}) {
	_, err := fmt.Fprint(w, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) Printf(format string, v ...interface{}) {
}
<commit_msg>app: add App.writef<commit_after>package main

import (
	"fmt"
	"io"
	"log"
	"os"
)

func main() {
	os.Exit(run(NewApp()))
}

const exitOk = iota

func run(app *App) int {
	return exitOk
}

// App represent application and
// holds configuration.
type App struct {
	osargs []string
	stdout io.Writer
	logger *log.Logger
}

// NewApp create application.
func NewApp() *App {
	return &App{
		osargs: os.Args,
		stdout: os.Stdout,
		logger: log.New(os.Stderr, "", 0)}
}

// Log prints to logger like log.Print.
func (a *App) Log(v ...interface{}) {
	a.logger.Print(v...)
}

// Logf prints to logger like log.Printf.
func (a *App) Logf(format string, v ...interface{}) {
	a.logger.Printf(format, v...)
}

// Logln prints to logger like log.Println.
func (a *App) Logln(v ...interface{}) {
	a.logger.Println(v...)
}

func (a *App) write(w io.Writer, v ...interface{}) {
	_, err := fmt.Fprint(w, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) writef(w io.Writer, format string, v ...interface{}) {
	_, err := fmt.Fprintf(w, format, v...)
	if err != nil {
		a.logger.Print(err)
	}
}

func (a *App) Printf(format string, v ...interface{}) {
}
<|endoftext|>