<commit_before>package diff

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/engine/apply/action"
	"github.com/Aptomi/aptomi/pkg/engine/apply/action/component"
	"github.com/Aptomi/aptomi/pkg/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/lang"
	"github.com/Aptomi/aptomi/pkg/lang/builder"
	"github.com/Aptomi/aptomi/pkg/util"
	"github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestDiffEmpty(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)
	resolvedNext := resolvePolicy(t, b)

	// diff should be empty
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 0, 0, 0, 0, 0, 0)
}

func TestDiffComponentCreationAndAttachDependency(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)

	// add dependency
	d1 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d1.Labels["param"] = "value1"
	resolvedNext := resolvePolicy(t, b)

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 2, 0, 0, 2, 0, 1)

	// add another dependency
	d2 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d2.Labels["param"] = "value1"
	resolvedNextAgain := resolvePolicy(t, b)

	// component should not be instantiated again (it's already there), just new dependency should be attached
	diffAgain := NewPolicyResolutionDiff(resolvedNextAgain, resolvedNext)
	verifyDiff(t, diffAgain, 0, 0, 0, 2, 0, 0)
}

func TestDiffComponentUpdate(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)

	// add dependency
	d1 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d1.Labels["param"] = "value1"
	resolvedNext := resolvePolicy(t, b)

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 2, 0, 0, 2, 0, 1)

	// update dependency
	d1.Labels["param"] = "value2"
	resolvedNextAgain := resolvePolicy(t, b)

	// component should be updated
	diffAgain := NewPolicyResolutionDiff(resolvedNextAgain, resolvedNext)
	verifyDiff(t, diffAgain, 0, 0, 2, 0, 0, 1)
}

func TestDiffComponentDelete(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)

	// add dependency
	d1 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d1.Labels["param"] = "value1"
	resolvedNext := resolvePolicy(t, b)

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 2, 0, 0, 2, 0, 1)

	// resolve empty policy
	resolvedEmpty := resolvePolicy(t, builder.NewPolicyBuilder())

	// diff should contain destructed component
	diffAgain := NewPolicyResolutionDiff(resolvedEmpty, resolvedNext)
	verifyDiff(t, diffAgain, 0, 2, 0, 0, 2, 0)
}

func TestDiffComponentWithServiceSharing(t *testing.T) {
	b := makePolicyBuilderWithServiceSharing()
	resolvedNext := resolvePolicy(t, b)
	resolvedEmpty := resolvePolicy(t, builder.NewPolicyBuilder())

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedEmpty)
	verifyDiff(t, diff, 7, 0, 0, 9, 0, 0)
}

/*
	Helpers
*/

func makePolicyBuilder() *builder.PolicyBuilder {
	b := builder.NewPolicyBuilder()

	// create a service
	service := b.AddService()
	b.AddServiceComponent(service,
		b.CodeComponent(
			util.NestedParameterMap{"param": "{{ .Labels.param }}"},
			nil,
		),
	)
	b.AddContract(service, b.CriteriaTrue())

	// add rule to set cluster
	clusterObj := b.AddCluster()
	b.AddRule(b.CriteriaTrue(), b.RuleActions(lang.NewLabelOperationsSetSingleLabel(lang.LabelCluster, clusterObj.Name)))

	return b
}

func makePolicyBuilderWithServiceSharing() *builder.PolicyBuilder {
	b := builder.NewPolicyBuilder()

	// create a service, which depends on another service
	service1 := b.AddService()
	contract1 := b.AddContract(service1, b.CriteriaTrue())
	service2 := b.AddService()
	contract2 := b.AddContract(service2, b.CriteriaTrue())
	b.AddServiceComponent(service1, b.ContractComponent(contract2))

	// make first service one per dependency, and they all will share the second service
	contract1.Contexts[0].Allocation.Keys = []string{"{{ .Dependency.ID }}"}

	// add rule to set cluster
	clusterObj := b.AddCluster()
	b.AddRule(b.CriteriaTrue(), b.RuleActions(lang.NewLabelOperationsSetSingleLabel(lang.LabelCluster, clusterObj.Name)))

	// add dependencies
	b.AddDependency(b.AddUser(), contract1)
	b.AddDependency(b.AddUser(), contract1)
	b.AddDependency(b.AddUser(), contract1)

	return b
}

func resolvePolicy(t *testing.T, builder *builder.PolicyBuilder) *resolve.PolicyResolution {
	t.Helper()
	eventLog := event.NewLog(logrus.WarnLevel, "test-resolve", false)
	resolver := resolve.NewPolicyResolver(builder.Policy(), builder.External(), eventLog)
	result := resolver.ResolveAllDependencies()
	if !assert.True(t, result.AllDependenciesResolvedSuccessfully(), "All dependencies should be resolved successfully") {
		hook := &event.HookConsole{}
		eventLog.Save(hook)
		t.FailNow()
	}
	return result
}

func verifyDiff(t *testing.T, diff *PolicyResolutionDiff, componentInstantiate int, componentDestruct int, componentUpdate int, componentAttachDependency int, componentDetachDependency int, componentEndpoints int) {
	t.Helper()
	cnt := struct {
		create    int
		update    int
		delete    int
		attach    int
		detach    int
		endpoints int
	}{}

	s := []string{}
	fn := func(act action.Base) error {
		switch act.(type) {
		case *component.CreateAction:
			cnt.create++
		case *component.DeleteAction:
			cnt.delete++
		case *component.UpdateAction:
			cnt.update++
		case *component.AttachDependencyAction:
			cnt.attach++
		case *component.DetachDependencyAction:
			cnt.detach++
		case *component.EndpointsAction:
			cnt.endpoints++
		default:
			t.Fatalf("Incorrect action type: %T", act)
		}
		s = append(s, fmt.Sprintf("\n%+v", act))
		return nil
	}

	_ = diff.ActionPlan.Apply(action.WrapSequential(fn), action.NewApplyResultUpdaterImpl())

	ok := assert.Equal(t, componentInstantiate, cnt.create, "Diff: component instantiations")
	ok = ok && assert.Equal(t, componentDestruct, cnt.delete, "Diff: component destructions")
	ok = ok && assert.Equal(t, componentUpdate, cnt.update, "Diff: component updates")
	ok = ok && assert.Equal(t, componentAttachDependency, cnt.attach, "Diff: dependencies attached to components")
	ok = ok && assert.Equal(t, componentDetachDependency, cnt.detach, "Diff: dependencies removed from components")
	ok = ok && assert.Equal(t, componentEndpoints, cnt.endpoints, "Diff: component endpoints")

	if !ok {
		t.Logf("Log of actions: %s", s)
		t.FailNow()
	}
}
<commit_msg>fix tests for endpoints<commit_after>package diff

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/engine/apply/action"
	"github.com/Aptomi/aptomi/pkg/engine/apply/action/component"
	"github.com/Aptomi/aptomi/pkg/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/event"
	"github.com/Aptomi/aptomi/pkg/lang"
	"github.com/Aptomi/aptomi/pkg/lang/builder"
	"github.com/Aptomi/aptomi/pkg/util"
	"github.com/Sirupsen/logrus"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestDiffEmpty(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)
	resolvedNext := resolvePolicy(t, b)

	// diff should be empty
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 0, 0, 0, 0, 0, 0)
}

func TestDiffComponentCreationAndAttachDependency(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)

	// add dependency
	d1 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d1.Labels["param"] = "value1"
	resolvedNext := resolvePolicy(t, b)

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 2, 0, 0, 2, 0, 1)

	// add another dependency
	d2 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d2.Labels["param"] = "value1"
	resolvedNextAgain := resolvePolicy(t, b)

	// component should not be instantiated again (it's already there), just new dependency should be attached
	diffAgain := NewPolicyResolutionDiff(resolvedNextAgain, resolvedNext)
	verifyDiff(t, diffAgain, 0, 0, 0, 2, 0, 1)
}

func TestDiffComponentUpdate(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)

	// add dependency
	d1 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d1.Labels["param"] = "value1"
	resolvedNext := resolvePolicy(t, b)

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 2, 0, 0, 2, 0, 1)

	// update dependency
	d1.Labels["param"] = "value2"
	resolvedNextAgain := resolvePolicy(t, b)

	// component should be updated
	diffAgain := NewPolicyResolutionDiff(resolvedNextAgain, resolvedNext)
	verifyDiff(t, diffAgain, 0, 0, 2, 0, 0, 1)
}

func TestDiffComponentDelete(t *testing.T) {
	b := makePolicyBuilder()
	resolvedPrev := resolvePolicy(t, b)

	// add dependency
	d1 := b.AddDependency(b.AddUser(), b.Policy().GetObjectsByKind(lang.ContractObject.Kind)[0].(*lang.Contract))
	d1.Labels["param"] = "value1"
	resolvedNext := resolvePolicy(t, b)

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedPrev)
	verifyDiff(t, diff, 2, 0, 0, 2, 0, 1)

	// resolve empty policy
	resolvedEmpty := resolvePolicy(t, builder.NewPolicyBuilder())

	// diff should contain destructed component
	diffAgain := NewPolicyResolutionDiff(resolvedEmpty, resolvedNext)
	verifyDiff(t, diffAgain, 0, 2, 0, 0, 2, 0)
}

func TestDiffComponentWithServiceSharing(t *testing.T) {
	b := makePolicyBuilderWithServiceSharing()
	resolvedNext := resolvePolicy(t, b)
	resolvedEmpty := resolvePolicy(t, builder.NewPolicyBuilder())

	// diff should contain instantiated component
	diff := NewPolicyResolutionDiff(resolvedNext, resolvedEmpty)
	verifyDiff(t, diff, 7, 0, 0, 9, 0, 0)
}

/*
	Helpers
*/

func makePolicyBuilder() *builder.PolicyBuilder {
	b := builder.NewPolicyBuilder()

	// create a service
	service := b.AddService()
	b.AddServiceComponent(service,
		b.CodeComponent(
			util.NestedParameterMap{"param": "{{ .Labels.param }}"},
			nil,
		),
	)
	b.AddContract(service, b.CriteriaTrue())

	// add rule to set cluster
	clusterObj := b.AddCluster()
	b.AddRule(b.CriteriaTrue(), b.RuleActions(lang.NewLabelOperationsSetSingleLabel(lang.LabelCluster, clusterObj.Name)))

	return b
}

func makePolicyBuilderWithServiceSharing() *builder.PolicyBuilder {
	b := builder.NewPolicyBuilder()

	// create a service, which depends on another service
	service1 := b.AddService()
	contract1 := b.AddContract(service1, b.CriteriaTrue())
	service2 := b.AddService()
	contract2 := b.AddContract(service2, b.CriteriaTrue())
	b.AddServiceComponent(service1, b.ContractComponent(contract2))

	// make first service one per dependency, and they all will share the second service
	contract1.Contexts[0].Allocation.Keys = []string{"{{ .Dependency.ID }}"}

	// add rule to set cluster
	clusterObj := b.AddCluster()
	b.AddRule(b.CriteriaTrue(), b.RuleActions(lang.NewLabelOperationsSetSingleLabel(lang.LabelCluster, clusterObj.Name)))

	// add dependencies
	b.AddDependency(b.AddUser(), contract1)
	b.AddDependency(b.AddUser(), contract1)
	b.AddDependency(b.AddUser(), contract1)

	return b
}

func resolvePolicy(t *testing.T, builder *builder.PolicyBuilder) *resolve.PolicyResolution {
	t.Helper()
	eventLog := event.NewLog(logrus.WarnLevel, "test-resolve", false)
	resolver := resolve.NewPolicyResolver(builder.Policy(), builder.External(), eventLog)
	result := resolver.ResolveAllDependencies()
	if !assert.True(t, result.AllDependenciesResolvedSuccessfully(), "All dependencies should be resolved successfully") {
		hook := &event.HookConsole{}
		eventLog.Save(hook)
		t.FailNow()
	}
	return result
}

func verifyDiff(t *testing.T, diff *PolicyResolutionDiff, componentInstantiate int, componentDestruct int, componentUpdate int, componentAttachDependency int, componentDetachDependency int, componentEndpoints int) {
	t.Helper()
	cnt := struct {
		create    int
		update    int
		delete    int
		attach    int
		detach    int
		endpoints int
	}{}

	s := []string{}
	fn := func(act action.Base) error {
		switch act.(type) {
		case *component.CreateAction:
			cnt.create++
		case *component.DeleteAction:
			cnt.delete++
		case *component.UpdateAction:
			cnt.update++
		case *component.AttachDependencyAction:
			cnt.attach++
		case *component.DetachDependencyAction:
			cnt.detach++
		case *component.EndpointsAction:
			cnt.endpoints++
		default:
			t.Fatalf("Incorrect action type: %T", act)
		}
		s = append(s, fmt.Sprintf("\n%+v", act))
		return nil
	}

	_ = diff.ActionPlan.Apply(action.WrapSequential(fn), action.NewApplyResultUpdaterImpl())

	ok := assert.Equal(t, componentInstantiate, cnt.create, "Diff: component instantiations")
	ok = ok && assert.Equal(t, componentDestruct, cnt.delete, "Diff: component destructions")
	ok = ok && assert.Equal(t, componentUpdate, cnt.update, "Diff: component updates")
	ok = ok && assert.Equal(t, componentAttachDependency, cnt.attach, "Diff: dependencies attached to components")
	ok = ok && assert.Equal(t, componentDetachDependency, cnt.detach, "Diff: dependencies removed from components")
	ok = ok && assert.Equal(t, componentEndpoints, cnt.endpoints, "Diff: component endpoints")

	if !ok {
		t.Logf("Log of actions: %s", s)
		t.FailNow()
	}
}
<|endoftext|>


<commit_before>package goref

import (
	"github.com/stretchr/testify/assert"
	"go/ast"
	"testing"
)

func TestCleanImportSpec(t *testing.T) {
	assert.Equal(t, "foo/bar/baz", cleanImportSpec(&ast.ImportSpec{Path: &ast.BasicLit{Value: "foo/bar/baz"}}))
	assert.Equal(t, "foo/bar/baz", cleanImportSpec(&ast.ImportSpec{Path: &ast.BasicLit{Value: "\"foo/bar/baz\""}}))
}
<commit_msg>Add a test for candidatePaths<commit_after>package goref

import (
	"github.com/stretchr/testify/assert"
	"go/ast"
	"testing"
)

func TestCleanImportSpec(t *testing.T) {
	assert.Equal(t, "foo/bar/baz", cleanImportSpec(&ast.ImportSpec{Path: &ast.BasicLit{Value: "foo/bar/baz"}}))
	assert.Equal(t, "foo/bar/baz", cleanImportSpec(&ast.ImportSpec{Path: &ast.BasicLit{Value: "\"foo/bar/baz\""}}))
}

func TestCandidatePaths(t *testing.T) {
	r := []string{
		"a/b/vendor/c/d",
		"a/vendor/c/d",
		"vendor/c/d",
		"c/d",
	}
	assert.Equal(t, r, candidatePaths("c/d", "a/b"))
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2015 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by the MIT license.
// See LICENSE file for details.

// Most of the code here is taken from the Google OAuth2 client library
// at https://github.com/golang/oauth2,
// especially https://github.com/golang/oauth2/blob/master/transport.go.
package balancers

import (
	"io"
	"net/http"
	"sync"
)

// Transport implements a http Transport for a HTTP load balancer.
type Transport struct {
	Base http.RoundTripper

	balancer Balancer

	mu     sync.Mutex
	modReq map[*http.Request]*http.Request
}

// RoundTrip is the core of the balancers package. It accepts a request,
// replaces host, scheme, and port with the URl provided by the balancer,
// executes it and returns the response to the caller.
func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
	conn, err := t.balancer.Get()
	if err != nil {
		return nil, err
	}

	rc := cloneRequest(r)
	if err := modifyRequest(rc, conn); err != nil {
		return nil, err
	}
	t.setModReq(r, rc)

	res, err := t.base().RoundTrip(rc)
	if err != nil {
		t.setModReq(r, nil)
		return nil, err
	}
	res.Body = &onEOFReader{
		rc: res.Body,
		fn: func() { t.setModReq(rc, nil) },
	}
	return res, nil
}

// CancelRequest cancels the given request (if canceling is available).
func (t *Transport) CancelRequest(r *http.Request) {
	type canceler interface {
		CancelRequest(*http.Request)
	}
	if cr, ok := t.base().(canceler); ok {
		t.mu.Lock()
		modReq := t.modReq[r]
		delete(t.modReq, r)
		t.mu.Unlock()
		cr.CancelRequest(modReq)
	}
}

func (t *Transport) base() http.RoundTripper {
	if t.Base != nil {
		return t.Base
	}
	return http.DefaultTransport
}

// modifyRequest exchanges the HTTP request scheme, host, and userinfo
// by the URL the connection returns.
func modifyRequest(r *http.Request, conn Connection) error {
	url := conn.URL()
	if url.Scheme != "" {
		r.URL.Scheme = url.Scheme
	}
	if url.Host != "" {
		r.URL.Host = url.Host
	}
	if url.User != nil {
		r.URL.User = url.User
	}
	return nil
}

// cloneRequest makes a duplicate of the request.
func cloneRequest(r *http.Request) *http.Request {
	rc := new(http.Request)
	*rc = *r
	rc.Header = make(http.Header, len(r.Header))
	for k, s := range r.Header {
		rc.Header[k] = append([]string(nil), s...)
	}
	return rc
}

func (t *Transport) setModReq(orig, mod *http.Request) {
	t.mu.Lock()
	defer t.mu.Unlock()

	if t.modReq == nil {
		t.modReq = make(map[*http.Request]*http.Request)
	}
	if mod == nil {
		delete(t.modReq, orig)
	} else {
		t.modReq[orig] = mod
	}
}

// onEOFReader is a reader that executes a function when io.EOF is read
// or the reader is closed.
type onEOFReader struct {
	rc io.ReadCloser
	fn func()
}

func (r *onEOFReader) Read(p []byte) (n int, err error) {
	n, err = r.rc.Read(p)
	if err == io.EOF {
		r.runFunc()
	}
	return
}

func (r *onEOFReader) Close() error {
	err := r.rc.Close()
	r.runFunc()
	return err
}

func (r *onEOFReader) runFunc() {
	if fn := r.fn; fn != nil {
		fn()
		r.fn = nil
	}
}

/*
type errorTransport struct{ err error }

func (t errorTransport) RoundTrip(*http.Request) (*http.Response, error) {
	return nil, t.err
}
*/
<commit_msg>Fix memory leak<commit_after>// Copyright (c) 2014-2015 Oliver Eilhard. All rights reserved.
// Use of this source code is governed by the MIT license.
// See LICENSE file for details.

// Most of the code here is taken from the Google OAuth2 client library
// at https://github.com/golang/oauth2,
// especially https://github.com/golang/oauth2/blob/master/transport.go.
package balancers

import (
	"io"
	"net/http"
	"sync"
)

// Transport implements a http Transport for a HTTP load balancer.
type Transport struct {
	Base http.RoundTripper

	balancer Balancer

	mu     sync.Mutex
	modReq map[*http.Request]*http.Request
}

// RoundTrip is the core of the balancers package. It accepts a request,
// replaces host, scheme, and port with the URl provided by the balancer,
// executes it and returns the response to the caller.
func (t *Transport) RoundTrip(r *http.Request) (*http.Response, error) {
	conn, err := t.balancer.Get()
	if err != nil {
		return nil, err
	}

	rc := cloneRequest(r)
	if err := modifyRequest(rc, conn); err != nil {
		return nil, err
	}
	t.setModReq(r, rc)

	res, err := t.base().RoundTrip(rc)
	if err != nil {
		t.setModReq(r, nil)
		return nil, err
	}
	res.Body = &onEOFReader{
		rc: res.Body,
		fn: func() { t.setModReq(r, nil) },
	}
	return res, nil
}

// CancelRequest cancels the given request (if canceling is available).
func (t *Transport) CancelRequest(r *http.Request) {
	type canceler interface {
		CancelRequest(*http.Request)
	}
	if cr, ok := t.base().(canceler); ok {
		t.mu.Lock()
		modReq := t.modReq[r]
		delete(t.modReq, r)
		t.mu.Unlock()
		cr.CancelRequest(modReq)
	}
}

func (t *Transport) base() http.RoundTripper {
	if t.Base != nil {
		return t.Base
	}
	return http.DefaultTransport
}

// modifyRequest exchanges the HTTP request scheme, host, and userinfo
// by the URL the connection returns.
func modifyRequest(r *http.Request, conn Connection) error {
	url := conn.URL()
	if url.Scheme != "" {
		r.URL.Scheme = url.Scheme
	}
	if url.Host != "" {
		r.URL.Host = url.Host
	}
	if url.User != nil {
		r.URL.User = url.User
	}
	return nil
}

// cloneRequest makes a duplicate of the request.
func cloneRequest(r *http.Request) *http.Request {
	rc := new(http.Request)
	*rc = *r
	rc.Header = make(http.Header, len(r.Header))
	for k, s := range r.Header {
		rc.Header[k] = append([]string(nil), s...)
	}
	return rc
}

func (t *Transport) setModReq(orig, mod *http.Request) {
	t.mu.Lock()
	defer t.mu.Unlock()

	if t.modReq == nil {
		t.modReq = make(map[*http.Request]*http.Request)
	}
	if mod == nil {
		delete(t.modReq, orig)
	} else {
		t.modReq[orig] = mod
	}
}

// onEOFReader is a reader that executes a function when io.EOF is read
// or the reader is closed.
type onEOFReader struct {
	rc io.ReadCloser
	fn func()
}

func (r *onEOFReader) Read(p []byte) (n int, err error) {
	n, err = r.rc.Read(p)
	if err == io.EOF {
		r.runFunc()
	}
	return
}

func (r *onEOFReader) Close() error {
	err := r.rc.Close()
	r.runFunc()
	return err
}

func (r *onEOFReader) runFunc() {
	if fn := r.fn; fn != nil {
		fn()
		r.fn = nil
	}
}

/*
type errorTransport struct{ err error }

func (t errorTransport) RoundTrip(*http.Request) (*http.Response, error) {
	return nil, t.err
}
*/
<|endoftext|>


<commit_before>package gui

import (
  "image"
  _ "image/png"
  _ "image/jpeg"
  "os"
  "gl"
  "gl/glu"
  "runtime"
  "glop/gin"
)

// An Anchor specifies where a widget should be positioned withing an AnchorBox
// All values are between 0 and 1, inclusive.  wx,wy represent a point on the widget,
// and bx,by represent a point on the AnchorBox.  During layout the widget will be positioned
// such that these points line up.
type Anchor struct {
  Wx,Wy,Bx,By float64
}

// An AnchorBox does layout according to Anchors.  An anchor must be specified when installing
// a widget.
type AnchorBox struct {
  EmbeddedWidget
  NonResponder
  NonThinker
  BasicZone
  children []Widget
  anchors  []Anchor
}
func MakeAnchorBox(dims Dims) *AnchorBox {
  var box AnchorBox
  box.EmbeddedWidget = &BasicWidget{ CoreWidget : &box }
  box.Request_dims = dims
  return &box
}
func (w *AnchorBox) String() string {
  return "anchor box"
}
func (w *AnchorBox) AddChild(widget Widget, anchor Anchor) {
  w.children = append(w.children, widget)
  w.anchors = append(w.anchors, anchor)
}
func (w *AnchorBox) RemoveChild(widget Widget) {
  for i := range w.children {
    if w.children[i] == widget {
      w.children[i] = w.children[len(w.children)-1]
      w.children = w.children[0 : len(w.children)-1]
      w.anchors[i] = w.anchors[len(w.anchors)-1]
      w.anchors = w.anchors[0 : len(w.anchors)-1]
      return
    }
  }
}
func (w *AnchorBox) GetChildren() []Widget {
  return w.children
}
func (w *AnchorBox) Draw(region Region) {
  w.Render_region = region
  for i := range w.children {
    widget := w.children[i]
    anchor := w.anchors[i]
    var child_region Region
    child_region.Dims = widget.Requested()
    xoff := int(anchor.Bx * float64(region.Dx) - anchor.Wx * float64(child_region.Dx) + 0.5)
    yoff := int(anchor.By * float64(region.Dy) - anchor.Wy * float64(child_region.Dy) + 0.5)
    if xoff < 0 {
      child_region.Dx += xoff
      xoff = 0
    }
    if yoff < 0 {
      child_region.Dy += yoff
      yoff = 0
    }
    if xoff + child_region.Dx > w.Render_region.Dx {
      child_region.Dx -= (xoff + child_region.Dx) - w.Render_region.Dx
    }
    if yoff + child_region.Dy > w.Render_region.Dy {
      child_region.Dy -= (yoff + child_region.Dy) - w.Render_region.Dy
    }
    child_region.X = xoff
    child_region.Y = yoff
    widget.Draw(child_region)
  }
}

type ImageBox struct {
  EmbeddedWidget
  NonResponder
  NonThinker
  BasicZone
  Childless

  active  bool
  texture gl.Texture
}
func MakeImageBox() *ImageBox {
  var ib ImageBox
  ib.EmbeddedWidget = &BasicWidget{ CoreWidget : &ib }
  runtime.SetFinalizer(&ib, freeTexture)
  return &ib
}
func (w *ImageBox) String() string {
  return "image box"
}
func freeTexture(w *ImageBox) {
  if w.active {
    w.texture.Delete()
  }
}
func (w *ImageBox) UnsetImage() {
  w.active = false
}
func (w *ImageBox) SetImageByTexture(texture gl.Texture, dx,dy int) {
  w.texture = texture
  w.Request_dims.Dx = dx
  w.Request_dims.Dy = dy
  w.active = true
}
func (w *ImageBox) SetImage(path string) {
  data,err := os.Open(path)
  if err != nil {
    // TODO: Log error
    return
  }

  var img image.Image
  img,_,err = image.Decode(data)
  if err != nil {
    // TODO: Log error
    return
  }

  w.Request_dims.Dx = img.Bounds().Dx()
  w.Request_dims.Dy = img.Bounds().Dy()
  canvas := image.NewRGBA(img.Bounds().Dx(), img.Bounds().Dy())
  for y := 0; y < canvas.Bounds().Dy(); y++ {
    for x := 0; x < canvas.Bounds().Dx(); x++ {
      r,g,b,a := img.At(x,y).RGBA()
      base := 4*x + canvas.Stride*y
      canvas.Pix[base]   = uint8(r)
      canvas.Pix[base+1] = uint8(g)
      canvas.Pix[base+2] = uint8(b)
      canvas.Pix[base+3] = uint8(a)
    }
  }

  w.texture = gl.GenTexture()
  gl.Enable(gl.TEXTURE_2D)
  w.texture.Bind(gl.TEXTURE_2D)
  gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
  glu.Build2DMipmaps(gl.TEXTURE_2D, 4, img.Bounds().Dx(), img.Bounds().Dy(), gl.RGBA, canvas.Pix)

  w.active = true
}
func (w *ImageBox) Draw(region Region) {
  w.Render_region = region
  if !w.active { return }
  gl.Enable(gl.TEXTURE_2D)
  w.texture.Bind(gl.TEXTURE_2D)
  gl.Color4d(1.0, 1.0, 1.0, 1.0)
  gl.Begin(gl.QUADS)
    gl.TexCoord2f(0, 0)
    gl.Vertex2i(region.X, region.Y)
    gl.TexCoord2f(0, -1)
    gl.Vertex2i(region.X, region.Y + region.Dy)
    gl.TexCoord2f(1, -1)
    gl.Vertex2i(region.X + region.Dx, region.Y + region.Dy)
    gl.TexCoord2f(1, 0)
    gl.Vertex2i(region.X + region.Dx, region.Y)
  gl.End()
  gl.Disable(gl.TEXTURE_2D)
}

type CollapseWrapper struct {
  EmbeddedWidget
  Wrapper
  CollapsableZone
  NonResponder
}

func MakeCollapseWrapper(w Widget) *CollapseWrapper {
  var cw CollapseWrapper
  cw.EmbeddedWidget = &BasicWidget{ CoreWidget : &cw }
  cw.Child = w
  return &cw
}

func (w *CollapseWrapper) String() string {
  return "collapse wrapper"
}

func (w *CollapseWrapper) DoThink(int64, bool) {
  w.Request_dims = w.Child.Requested()
  w.Render_region = w.Child.Rendered()
}

func (w *CollapseWrapper) Draw(region Region) {
  if w.Collapsed {
    w.Render_region = Region{}
    return
  }
  w.Child.Draw(region)
  w.Render_region = region
}

type SelectTextOption struct {
  TextLine
  r,g,b,a     float64
  rh,gh,bh,ah float64
}

func makeSelectTextOption(text string, width int, r,g,b,a, rh,gh,bh,ah float64) *SelectTextOption {
  var sto SelectTextOption
  sto.TextLine = *MakeTextLine("standard", text, width, r, g, b, a)
  sto.r, sto.g, sto.b, sto.a = r, g, b, a
  sto.rh, sto.gh, sto.bh, sto.ah = rh, gh, bh, ah
  sto.EmbeddedWidget = &BasicWidget{ CoreWidget : &sto }
  return &sto
}

func (w *SelectTextOption) DoThink(int64,bool) {
  x,y := gin.In().GetCursor("Mouse").Point()
  p := Point{ x, y }
  if p.Inside(w.Render_region) {
    w.SetColor(w.rh, w.gh, w.bh, w.ah)
  } else {
    w.SetColor(w.r, w.g, w.b, w.a)
  }
}

func (w *SelectTextOption) Draw(region Region) {
  w.TextLine.Draw(region)
}

type SelectTextBox struct {
  VerticalTable
  options  []string
  selected int
}

func MakeSelectTextBox(options []string, width int) *SelectTextBox {
  var stb SelectTextBox
  stb.VerticalTable = *MakeVerticalTable()
  stb.EmbeddedWidget = &BasicWidget{ CoreWidget : &stb }
  for i := range options {
    stb.VerticalTable.AddChild(makeSelectTextOption(options[i], width, 1, 1, 1, 1, 1, 0, 0, 1))
  }
  return &stb
}

func (w *SelectTextBox) String() string {
  return "select text box"
}

func (w *SelectTextBox) DoRespond(event_group EventGroup) (consume,change_focus bool) {
  if event_group.Events[0].Type != gin.Press { return }
  if event_group.Events[0].Key.Id() != gin.MouseLButton { return }
  x,y := event_group.Events[0].Key.Cursor().Point()
  p := Point{ x, y }
  w.selected = -1
  for i := range w.Children {
    if p.Inside(w.Rendered()) {
      w.selected = i
      break
    }
  }
  return
}

<commit_msg>Got a decent select box working<commit_after>package gui

import (
  "image"
  _ "image/png"
  _ "image/jpeg"
  "os"
  "gl"
  "gl/glu"
  "runtime"
  "glop/gin"
)

// An Anchor specifies where a widget should be positioned withing an AnchorBox
// All values are between 0 and 1, inclusive.  wx,wy represent a point on the widget,
// and bx,by represent a point on the AnchorBox.  During layout the widget will be positioned
// such that these points line up.
type Anchor struct {
  Wx,Wy,Bx,By float64
}

// An AnchorBox does layout according to Anchors.  An anchor must be specified when installing
// a widget.
type AnchorBox struct {
  EmbeddedWidget
  NonResponder
  NonThinker
  BasicZone
  children []Widget
  anchors  []Anchor
}
func MakeAnchorBox(dims Dims) *AnchorBox {
  var box AnchorBox
  box.EmbeddedWidget = &BasicWidget{ CoreWidget : &box }
  box.Request_dims = dims
  return &box
}
func (w *AnchorBox) String() string {
  return "anchor box"
}
func (w *AnchorBox) AddChild(widget Widget, anchor Anchor) {
  w.children = append(w.children, widget)
  w.anchors = append(w.anchors, anchor)
}
func (w *AnchorBox) RemoveChild(widget Widget) {
  for i := range w.children {
    if w.children[i] == widget {
      w.children[i] = w.children[len(w.children)-1]
      w.children = w.children[0 : len(w.children)-1]
      w.anchors[i] = w.anchors[len(w.anchors)-1]
      w.anchors = w.anchors[0 : len(w.anchors)-1]
      return
    }
  }
}
func (w *AnchorBox) GetChildren() []Widget {
  return w.children
}
func (w *AnchorBox) Draw(region Region) {
  w.Render_region = region
  for i := range w.children {
    widget := w.children[i]
    anchor := w.anchors[i]
    var child_region Region
    child_region.Dims = widget.Requested()
    xoff := int(anchor.Bx * float64(region.Dx) - anchor.Wx * float64(child_region.Dx) + 0.5)
    yoff := int(anchor.By * float64(region.Dy) - anchor.Wy * float64(child_region.Dy) + 0.5)
    if xoff < 0 {
      child_region.Dx += xoff
      xoff = 0
    }
    if yoff < 0 {
      child_region.Dy += yoff
      yoff = 0
    }
    if xoff + child_region.Dx > w.Render_region.Dx {
      child_region.Dx -= (xoff + child_region.Dx) - w.Render_region.Dx
    }
    if yoff + child_region.Dy > w.Render_region.Dy {
      child_region.Dy -= (yoff + child_region.Dy) - w.Render_region.Dy
    }
    child_region.X = xoff
    child_region.Y = yoff
    widget.Draw(child_region)
  }
}

type ImageBox struct {
  EmbeddedWidget
  NonResponder
  NonThinker
  BasicZone
  Childless

  active  bool
  texture gl.Texture
}
func MakeImageBox() *ImageBox {
  var ib ImageBox
  ib.EmbeddedWidget = &BasicWidget{ CoreWidget : &ib }
  runtime.SetFinalizer(&ib, freeTexture)
  return &ib
}
func (w *ImageBox) String() string {
  return "image box"
}
func freeTexture(w *ImageBox) {
  if w.active {
    w.texture.Delete()
  }
}
func (w *ImageBox) UnsetImage() {
  w.active = false
}
func (w *ImageBox) SetImageByTexture(texture gl.Texture, dx,dy int) {
  w.texture = texture
  w.Request_dims.Dx = dx
  w.Request_dims.Dy = dy
  w.active = true
}
func (w *ImageBox) SetImage(path string) {
  data,err := os.Open(path)
  if err != nil {
    // TODO: Log error
    return
  }

  var img image.Image
  img,_,err = image.Decode(data)
  if err != nil {
    // TODO: Log error
    return
  }

  w.Request_dims.Dx = img.Bounds().Dx()
  w.Request_dims.Dy = img.Bounds().Dy()
  canvas := image.NewRGBA(img.Bounds().Dx(), img.Bounds().Dy())
  for y := 0; y < canvas.Bounds().Dy(); y++ {
    for x := 0; x < canvas.Bounds().Dx(); x++ {
      r,g,b,a := img.At(x,y).RGBA()
      base := 4*x + canvas.Stride*y
      canvas.Pix[base]   = uint8(r)
      canvas.Pix[base+1] = uint8(g)
      canvas.Pix[base+2] = uint8(b)
      canvas.Pix[base+3] = uint8(a)
    }
  }

  w.texture = gl.GenTexture()
  gl.Enable(gl.TEXTURE_2D)
  w.texture.Bind(gl.TEXTURE_2D)
  gl.TexEnvf(gl.TEXTURE_ENV, gl.TEXTURE_ENV_MODE, gl.MODULATE)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT)
  gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT)
  glu.Build2DMipmaps(gl.TEXTURE_2D, 4, img.Bounds().Dx(), img.Bounds().Dy(), gl.RGBA, canvas.Pix)

  w.active = true
}
func (w *ImageBox) Draw(region Region) {
  w.Render_region = region
  if !w.active { return }
  gl.Enable(gl.TEXTURE_2D)
  w.texture.Bind(gl.TEXTURE_2D)
  gl.Color4d(1.0, 1.0, 1.0, 1.0)
  gl.Begin(gl.QUADS)
    gl.TexCoord2f(0, 0)
    gl.Vertex2i(region.X, region.Y)
    gl.TexCoord2f(0, -1)
    gl.Vertex2i(region.X, region.Y + region.Dy)
    gl.TexCoord2f(1, -1)
    gl.Vertex2i(region.X + region.Dx, region.Y + region.Dy)
    gl.TexCoord2f(1, 0)
    gl.Vertex2i(region.X + region.Dx, region.Y)
  gl.End()
  gl.Disable(gl.TEXTURE_2D)
}

type CollapseWrapper struct {
  EmbeddedWidget
  Wrapper
  CollapsableZone
  NonResponder
}

func MakeCollapseWrapper(w Widget) *CollapseWrapper {
  var cw CollapseWrapper
  cw.EmbeddedWidget = &BasicWidget{ CoreWidget : &cw }
  cw.Child = w
  return &cw
}

func (w *CollapseWrapper) String() string {
  return "collapse wrapper"
}

func (w *CollapseWrapper) DoThink(int64, bool) {
  w.Request_dims = w.Child.Requested()
  w.Render_region = w.Child.Rendered()
}

func (w *CollapseWrapper) Draw(region Region) {
  if w.Collapsed {
    w.Render_region = Region{}
    return
  }
  w.Child.Draw(region)
  w.Render_region = region
}

type SelectTextOption struct {
  TextLine
}

func makeSelectTextOption(text string, width int) *SelectTextOption {
  var sto SelectTextOption
  sto.TextLine = *MakeTextLine("standard", text, width, 1, 1, 1, 1)
  sto.EmbeddedWidget = &BasicWidget{ CoreWidget : &sto }
  return &sto
}

func (w *SelectTextOption) Draw(region Region) {
  w.TextLine.Draw(region)
}

type SelectTextBox struct {
  VerticalTable
  selected int
}

func MakeSelectTextBox(options []string, width int) *SelectTextBox {
  var stb SelectTextBox
  stb.VerticalTable = *MakeVerticalTable()
  stb.EmbeddedWidget = &BasicWidget{ CoreWidget : &stb }
  for i := range options {
    stb.VerticalTable.AddChild(makeSelectTextOption(options[i], width))
  }
  stb.selected = -1
  return &stb
}

func (w *SelectTextBox) String() string {
  return "select text box"
}

func (w *SelectTextBox) GetSelectedIndex() int {
  return w.selected
}

func (w *SelectTextBox) GetSelectedOption() string {
  if w.selected == -1 { return "" }
  return w.Children[w.selected].(*SelectTextOption).GetText()
}

func (w *SelectTextBox) DoRespond(event_group EventGroup) (consume,change_focus bool) {
  if event_group.Events[0].Type != gin.Press { return }
  if event_group.Events[0].Key.Id() != gin.MouseLButton { return }
  x,y := event_group.Events[0].Key.Cursor().Point()
  p := Point{ x, y }
  for i := range w.Children {
    if p.Inside(w.Children[i].Rendered()) {
      if w.selected >= 0 {
        w.Children[w.selected].(*SelectTextOption).SetColor(1, 1, 1, 1)
      }
      w.Children[i].(*SelectTextOption).SetColor(1, 0, 0, 1)
      w.selected = i
      break
    }
  }
  return
}

<|endoftext|>


<commit_before>package main

import (
	"os"
	"testing"
	"time"
	"bytes"
	"io"
  "path"
)

// change traq env to use our fixtures
func WithFakeEnv(block func()) {
  oldEnv := os.Getenv("TRAQ_DATA_DIR")
  path, _ := os.Getwd()
  os.Setenv("TRAQ_DATA_DIR", path + "/fixtures")

  block()

  os.Setenv("TRAQ_DATA_DIR", oldEnv)
}

// capture output written to os.Stdout and return it
func CaptureStdout(block func()) string {
  old := os.Stdout // keep backup of the real stdout
  r, w, _ := os.Pipe()
  os.Stdout = w

  block()

  outC := make(chan string)
  // copy the output in a separate goroutine so printing can't block indefinitely
  go func() {
      var buf bytes.Buffer
      io.Copy(&buf, r)
      outC <- buf.String()
  }()

  // back to normal state
  w.Close()
  os.Stdout = old

  return <-outC
}

func TestDatesInMonth(t *testing.T) {
  dates := DatesInMonth(1986, 9)

  if len(dates) != 30 {
    t.Errorf("expected 30 days in Sep 1986, got %v", len(dates))
  }

  if dates[0].Weekday() != time.Monday {
    t.Errorf("Started on a Monday, got %v", dates[0].Weekday())
  }

  if dates[len(dates) - 1].Weekday() != time.Tuesday {
    t.Errorf("Ended on a Tuesday, got %v", dates[len(dates) - 1].Weekday())
  }
}

func TestPrintDate(t *testing.T) {
  out := CaptureStdout(func() {
    WithFakeEnv(func() {
      PrintDate("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))
    })
  })

  expected :=
`Wed Sep 03 20:00:00 +0100 1986;#birth;comment
Wed Sep 03 21:45:33 +0100 1986;#chillout;
Wed Sep 03 23:24:49 +0100 1986;stop;
%%
`
  if out != expected {
    t.Errorf("unexpected PrintDate output. Expected '%v' got '%v'", expected, out)
  }
}

func TestEvaluateDate(t *testing.T) {
  out := CaptureStdout(func() {
    WithFakeEnv(func() {
      EvaluateDate(ContentLoader, "example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))
    })
  })

  expected :=
`1986-09-03
#birth:1.7592
#chillout:1.6544
%%
`

  if out != expected {
    t.Errorf("unexpected EvaluateDate output. Expected '%v' got '%v'", expected, out)
  }
}

func TestEntry(t *testing.T) {
  expected := `Wed Sep 3 12:00:00 +0000 1986;#test;
`

  if entry := Entry(time.Date(1986, 9, 3, 12, 0, 0, 0, time.UTC), "#test"); entry != expected {
    t.Errorf("got wrong entry. Expected '%v' got '%v'", expected, entry)
  }
}

func TestWriteToFile(t *testing.T) {
  startDate := time.Date(2013, 1, 3, 12, 30, 0, 0, time.UTC)
  endDate := time.Date(2013, 1, 3, 13, 30, 0, 0, time.UTC)

  WithFakeEnv(func() {
    WriteToFile("example", startDate, "test")

    filePath := FilePath("example", startDate)
    out, _ := ContentLoader(filePath)
    if len(out) != 2 {
      t.Errorf("Expected different line count. Got %v", len(out))
    }

    if out[0] != "Thu Jan 3 12:30:00 +0000 2013;#test;" {
      t.Errorf("Expected different first line. Got %v", out[0])
    }

    WriteToFile("example", endDate, "stop")
    out, _ = ContentLoader(filePath)

    if len(out) != 3 {
      t.Errorf("Expected different line count. Got %v", len(out))
    }
    if out[1] != "Thu Jan 3 13:30:00 +0000 2013;stop;" {
      t.Errorf("Expected different stop line. Got %v", out[1])
    }

    os.RemoveAll(path.Dir(filePath))
  })
}

func TestRunningLoader(t *testing.T) {
  startDate := time.Date(2013, 1, 3, 12, 30, 0, 0, time.UTC)

  WithFakeEnv(func() {
    WriteToFile("example", startDate, "test")

    filePath := FilePath("example", startDate)
    out, _ := RunningLoader(filePath)
    if len(out) != 3 {
      t.Errorf("Expected different line count. Got %v", len(out))
    }

    os.RemoveAll(path.Dir(filePath))
  })
}

func TestFilePath(t *testing.T) {
	var path string = FilePath("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))

	if path != os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03" {
		t.Errorf("FilePath = %v, want %v", path, os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03")
	}
}

func TestEmptySumFile(t *testing.T) {
	content := []string{""}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if ok {
			t.Errorf("summed['#work'] = %v, should not exist", total)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestSimpleSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestNoStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
func TestWithStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;stop;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
<commit_msg>update tests to please travis<commit_after>package main

import (
	"bytes"
	"io"
	"os"
	"path"
	"strings"
	"testing"
	"time"
)

// change traq env to use our fixtures
func WithFakeEnv(block func()) {
	oldEnv := os.Getenv("TRAQ_DATA_DIR")
	path, _ := os.Getwd()
	os.Setenv("TRAQ_DATA_DIR", path+"/fixtures")

	block()

	os.Setenv("TRAQ_DATA_DIR", oldEnv)
}

// capture output written to os.Stdout and return it
func CaptureStdout(block func()) string {
	old := os.Stdout // keep backup of the real stdout
	r, w, _ := os.Pipe()
	os.Stdout = w

	block()

	outC := make(chan string)
	// copy the output in a separate goroutine so printing can't block indefinitely
	go func() {
		var buf bytes.Buffer
		io.Copy(&buf, r)
		outC <- buf.String()
	}()

	// back to normal state
	w.Close()
	os.Stdout = old

	return <-outC
}

func TestDatesInMonth(t *testing.T) {
	dates := DatesInMonth(1986, 9)

	if len(dates) != 30 {
		t.Errorf("expected 30 days in Sep 1986, got %v", len(dates))
	}

	if dates[0].Weekday() != time.Monday {
		t.Errorf("Started on a Monday, got %v", dates[0].Weekday())
	}

	if dates[len(dates)-1].Weekday() != time.Tuesday {
		t.Errorf("Ended on a Tuesday, got %v", dates[len(dates)-1].Weekday())
	}
}

func TestPrintDate(t *testing.T) {
	out := CaptureStdout(func() {
		WithFakeEnv(func() {
			PrintDate("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))
		})
	})

	expected :=
		`Wed Sep 03 20:00:00 +0100 1986;#birth;comment
Wed Sep 03 21:45:33 +0100 1986;#chillout;
Wed Sep 03 23:24:49 +0100 1986;stop;
%%
`
	if out != expected {
		t.Errorf("unexpected PrintDate output. Expected '%v' got '%v'", expected, out)
	}
}

func TestEvaluateDate(t *testing.T) {
	out := CaptureStdout(func() {
		WithFakeEnv(func() {
			EvaluateDate(ContentLoader, "example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))
		})
	})

	expectedLines := map[string]bool{
		"1986-09-03":       false,
		"#birth:1.7592":    false,
		"#chillout:1.6544": false,
	}
	for _, line := range strings.Split(out, "\n") {
		expectedLines[line] = true
	}
	for key, present := range expectedLines {
		if !present {
			t.Errorf("unexpected EvaluateDate output. Expected '%v', missing from '%v'", key, out)
		}
	}
}

func TestEntry(t *testing.T) {
	expected := `Wed Sep 3 12:00:00 +0000 1986;#test;
`

	if entry := Entry(time.Date(1986, 9, 3, 12, 0, 0, 0, time.UTC), "#test"); entry != expected {
		t.Errorf("got wrong entry. Expected '%v' got '%v'", expected, entry)
	}
}

func TestWriteToFile(t *testing.T) {
	startDate := time.Date(2013, 1, 3, 12, 30, 0, 0, time.UTC)
	endDate := time.Date(2013, 1, 3, 13, 30, 0, 0, time.UTC)

	WithFakeEnv(func() {
		WriteToFile("example", startDate, "test")

		filePath := FilePath("example", startDate)
		out, _ := ContentLoader(filePath)
		if len(out) != 2 {
			t.Errorf("Expected different line count. Got %v", len(out))
		}

		if out[0] != "Thu Jan 3 12:30:00 +0000 2013;#test;" {
			t.Errorf("Expected different first line. Got %v", out[0])
		}

		WriteToFile("example", endDate, "stop")
		out, _ = ContentLoader(filePath)

		if len(out) != 3 {
			t.Errorf("Expected different line count. Got %v", len(out))
		}
		if out[1] != "Thu Jan 3 13:30:00 +0000 2013;stop;" {
			t.Errorf("Expected different stop line. Got %v", out[1])
		}

		os.RemoveAll(path.Dir(filePath))
	})
}

func TestRunningLoader(t *testing.T) {
	startDate := time.Date(2013, 1, 3, 12, 30, 0, 0, time.UTC)

	WithFakeEnv(func() {
		WriteToFile("example", startDate, "test")

		filePath := FilePath("example", startDate)
		out, _ := RunningLoader(filePath)
		if len(out) != 3 {
			t.Errorf("Expected different line count. Got %v", len(out))
		}

		os.RemoveAll(path.Dir(filePath))
	})
}

func TestFilePath(t *testing.T) {
	var path string = FilePath("example", time.Date(1986, 9, 3, 0, 0, 0, 0, time.UTC))

	if path != os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03" {
		t.Errorf("FilePath = %v, want %v", path, os.Getenv("TRAQ_DATA_DIR")+"/example/1986/1986-09-03")
	}
}

func TestEmptySumFile(t *testing.T) {
	content := []string{""}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if ok {
			t.Errorf("summed['#work'] = %v, should not exist", total)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestSimpleSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}

func TestNoStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
func TestWithStopSumFile(t *testing.T) {
	content := []string{
		"Mon Oct 28 20:00:00 +0100 2013;#play;",
		"Mon Oct 28 21:45:33 +0100 2013;stop;",
		"Mon Oct 28 21:45:33 +0100 2013;#work;",
		"Mon Oct 28 23:24:49 +0100 2013;stop;",
	}
	var summed, error = SumFile(content)

	if error == nil {
		var total, ok = summed["#play"]
		if total != 6333 || !ok {
			t.Errorf("summed['#play'] = %v, want %v", total, 6333)
		}
		total, ok = summed["#work"]
		if total != 5956 || !ok {
			t.Errorf("summed['#work'] = %v, want %v", total, 5956)
		}
	} else {
		t.Errorf("parsing error %v", error)
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"github.com/astaxie/beego"
	"github.com/scmo/apayment-backend/models"
	"encoding/json"
	"github.com/scmo/apayment-backend/services"
	"strconv"
	"github.com/scmo/apayment-backend/ethereum"
	"math/big"
)

// Operations about Contributions
type RequestController struct {
	beego.Controller
}

// @Title Create a new Request
// @Description Endpoint to create a new Request
// @Param	body		body 	models.Request	true		"body for request content"
// @Success 200 {Object} models.Request
// @Failure 403 body is empty
// @router / [post]
func (this *RequestController) Post() {
	var request models.Request
	json.Unmarshal(this.Ctx.Input.RequestBody, &request)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}
	request.User = user

	err = services.CreateRequest(&request, ethereum.GetAuth(user.Address))
	if err != nil {
		this.CustomAbort(500, err.Error())
	}
	//go func() {
	//	// wait till contract created
	//	time.Sleep(time.Minute * 1)
	//	// Update GVE for the request
	//	err = services.SetGVE(&request)
	//	if (err != nil ) {
	//		this.CustomAbort(500, err.Error())
	//	}
	//}()

	this.ServeJSON()
}

// @Title Get
// @Description find request by requestID
// @Param jwtToken header string true "jwt Token for Authorization"
// @Param	requestId		path 	string	true		"the requestid you want to get"
// @Success 200 {object} models.Request
// @Failure 403 :requestId is empty
// @router /:requestId [get]
func (this *RequestController) Get() {
	input := this.Ctx.Input.Param(":requestId")
	requestId, err := strconv.ParseInt(input, 10, 64)
	if err != nil {
		beego.Error(err)
	}
	this.Data["json"] = services.GetRequestById(requestId, true)
	this.ServeJSON()
}

// @Title GetAll
// @Description get all request
// @Success 200 {object} models.Request
// @router / [get]
func (this *RequestController) GetAll() {
	requests := []*models.Request{}

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if (user.HasRole("Farmer")) {
		requests = services.GetAllRequestsByUserId(user.Id)
	} else if ( user.HasRole("Admin") || user.HasRole("Canton")) {
		requests = services.GetAllRequests()
	} else {
		this.CustomAbort(401, "Unauthorized")
	}

	this.Data["json"] = requests
	this.ServeJSON()
}

// @Title GetAll
// @Description get all request which have an inspector assigned
// @Success 200 {object} models.Request
// @router /inspection [get]
func (this *RequestController) GetAllForInspection() {
	requests := []*models.Request{}
	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if (user.HasRole("Inspector")) {
		requests = services.GetAllRequestsForInspectionByInspectorId(user.Id)
	} else if ( user.HasRole("Admin") || user.HasRole("Canton")) {
		requests = services.GetAllRequestsForInspection()
	} else {
		this.CustomAbort(401, "Unauthorized")
	}

	this.Data["json"] = requests
	this.ServeJSON()
}

// @Title Add Inspector
// @Description add Inspector to Requestion
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @router /inspector [put]
func (this *RequestController) AddInspector() {
	var request models.Request
	json.Unmarshal(this.Ctx.Input.RequestBody, &request)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if ( user.HasRole("Admin") || user.HasRole("Canton")) {
		//requests = services.GetAllRequests()
		services.AddInspectorToRequest(&request, ethereum.GetAuth(user.Address))
	} else {
		this.CustomAbort(401, "Unauthorized")
	}

	this.Data["json"] = request
	this.ServeJSON()
}

// @Title Add Inspection
// @Description Add the report of the inspection
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @router /inspection [post]
func (this *RequestController) AddInspection() {
	var inspection models.Inspection
	json.Unmarshal(this.Ctx.Input.RequestBody, &inspection)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}
	if ( user.HasRole("Admin") || user.HasRole("Inspector")) {
		//inspection.InspectorId = user.Id
	} else {
		this.CustomAbort(401, "Unauthorized")
	}
	err = services.AddLacksToRequest(&inspection, ethereum.GetAuth(user.Address))
	if err != nil {
		this.CustomAbort(500, err.Error())
	}
	this.Data["json"] = inspection
	this.ServeJSON()
}

// @Title Update GVE
// @Description Update GVE of request
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @Failure 403 :requestId is empty
// @router /gve [put]
func (this *RequestController) UpdateGVE() {
	var request models.Request

	json.Unmarshal(this.Ctx.Input.RequestBody, &request)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if ( ( user.HasRole("Admin") || user.HasRole("Canton") ) || user.Address == request.User.Address) {
		err = services.SetGVE(&request)
		if (err != nil ) {
			this.CustomAbort(500, err.Error())
		}
	}

	this.Data["json"] = request
	this.ServeJSON()
}

// @Title Pay DirectPayment
// @Description Update GVE of request
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @router /pay [post]
func (this *RequestController) Pay() {

	var r models.Request
	json.Unmarshal(this.Ctx.Input.RequestBody, &r)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if ( ( user.HasRole("Admin") || user.HasRole("Canton") ) == false ) {
		this.CustomAbort(401, "Unauthorized")
	}

	request := services.GetRequestById(r.Id, true)

	apaymentTransfer := &models.APaymentTokenTransfer{
		From:user.Address,
		To: request.User.Address,
	}
	if ( len(request.Payments) == 0 ) {
		beego.Debug("make first payment")
		amount, err := services.GetFirstPaymentAmount(request)
		beego.Info("GetFirstPaymentAmount: ", amount)
		if (err != nil) {
			beego.Error("Error while getting first payment amount. ", err)
			this.CustomAbort(500, err.Error())
		}
		amount.Div(amount, big.NewInt(2)) // 50% of the amount

		apaymentTransfer.Amount = amount
		apaymentTransfer.Message = "First Payment"
		err = services.Transfer(apaymentTransfer, request.Address)
		if err != nil {
			beego.Debug("Error while transfer", err)
			this.CustomAbort(500, err.Error())
		}
	} else if ( len(request.Payments) == 1 ) {
		beego.Debug("make second payment")
		//services.AddPayment(request, common.HexToAddress(user.Address), big.NewInt(333))
	} else if ( len(request.Payments) == 2 ) {
		beego.Debug("make third payment")
	}

	//request = services.GetRequestById(r.Id)
	this.Data["json"] = request
	this.ServeJSON()
}<commit_msg>replace address with EthereumAddress<commit_after>package controllers

import (
	"github.com/astaxie/beego"
	"github.com/scmo/apayment-backend/models"
	"encoding/json"
	"github.com/scmo/apayment-backend/services"
	"strconv"
	"github.com/scmo/apayment-backend/ethereum"
	"math/big"
)

// Operations about Contributions
type RequestController struct {
	beego.Controller
}

// @Title Create a new Request
// @Description Endpoint to create a new Request
// @Param	body		body 	models.Request	true		"body for request content"
// @Success 200 {Object} models.Request
// @Failure 403 body is empty
// @router / [post]
func (this *RequestController) Post() {
	var request models.Request
	json.Unmarshal(this.Ctx.Input.RequestBody, &request)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}
	request.User = user

	err = services.CreateRequest(&request, ethereum.GetAuth(user.EtherumAddress))
	if err != nil {
		this.CustomAbort(500, err.Error())
	}
	//go func() {
	//	// wait till contract created
	//	time.Sleep(time.Minute * 1)
	//	// Update GVE for the request
	//	err = services.SetGVE(&request)
	//	if (err != nil ) {
	//		this.CustomAbort(500, err.Error())
	//	}
	//}()

	this.ServeJSON()
}

// @Title Get
// @Description find request by requestID
// @Param jwtToken header string true "jwt Token for Authorization"
// @Param	requestId		path 	string	true		"the requestid you want to get"
// @Success 200 {object} models.Request
// @Failure 403 :requestId is empty
// @router /:requestId [get]
func (this *RequestController) Get() {
	input := this.Ctx.Input.Param(":requestId")
	requestId, err := strconv.ParseInt(input, 10, 64)
	if err != nil {
		beego.Error(err)
	}
	this.Data["json"] = services.GetRequestById(requestId, true)
	this.ServeJSON()
}

// @Title GetAll
// @Description get all request
// @Success 200 {object} models.Request
// @router / [get]
func (this *RequestController) GetAll() {
	requests := []*models.Request{}

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if (user.HasRole("Farmer")) {
		requests = services.GetAllRequestsByUserId(user.Id)
	} else if ( user.HasRole("Admin") || user.HasRole("Canton")) {
		requests = services.GetAllRequests()
	} else {
		this.CustomAbort(401, "Unauthorized")
	}

	this.Data["json"] = requests
	this.ServeJSON()
}

// @Title GetAll
// @Description get all request which have an inspector assigned
// @Success 200 {object} models.Request
// @router /inspection [get]
func (this *RequestController) GetAllForInspection() {
	requests := []*models.Request{}
	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if (user.HasRole("Inspector")) {
		requests = services.GetAllRequestsForInspectionByInspectorId(user.Id)
	} else if ( user.HasRole("Admin") || user.HasRole("Canton")) {
		requests = services.GetAllRequestsForInspection()
	} else {
		this.CustomAbort(401, "Unauthorized")
	}

	this.Data["json"] = requests
	this.ServeJSON()
}

// @Title Add Inspector
// @Description add Inspector to Requestion
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @router /inspector [put]
func (this *RequestController) AddInspector() {
	var request models.Request
	json.Unmarshal(this.Ctx.Input.RequestBody, &request)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if ( user.HasRole("Admin") || user.HasRole("Canton")) {
		//requests = services.GetAllRequests()
		services.AddInspectorToRequest(&request, ethereum.GetAuth(user.EtherumAddress))
	} else {
		this.CustomAbort(401, "Unauthorized")
	}

	this.Data["json"] = request
	this.ServeJSON()
}

// @Title Add Inspection
// @Description Add the report of the inspection
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @router /inspection [post]
func (this *RequestController) AddInspection() {
	var inspection models.Inspection
	json.Unmarshal(this.Ctx.Input.RequestBody, &inspection)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}
	if ( user.HasRole("Admin") || user.HasRole("Inspector")) {
		//inspection.InspectorId = user.Id
	} else {
		this.CustomAbort(401, "Unauthorized")
	}
	err = services.AddLacksToRequest(&inspection, ethereum.GetAuth(user.EtherumAddress))
	if err != nil {
		this.CustomAbort(500, err.Error())
	}
	this.Data["json"] = inspection
	this.ServeJSON()
}

// @Title Update GVE
// @Description Update GVE of request
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @Failure 403 :requestId is empty
// @router /gve [put]
func (this *RequestController) UpdateGVE() {
	var request models.Request

	json.Unmarshal(this.Ctx.Input.RequestBody, &request)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if ( ( user.HasRole("Admin") || user.HasRole("Canton") ) || user.EtherumAddress == request.User.EtherumAddress) {
		err = services.SetGVE(&request)
		if (err != nil ) {
			this.CustomAbort(500, err.Error())
		}
	}

	this.Data["json"] = request
	this.ServeJSON()
}

// @Title Pay DirectPayment
// @Description Update GVE of request
// @Param	body		body 	models.Request	true		"body for requestion content"
// @Success 200 {object} models.Request
// @router /pay [post]
func (this *RequestController) Pay() {

	var r models.Request
	json.Unmarshal(this.Ctx.Input.RequestBody, &r)

	claims, _ := services.ParseToken(this.Ctx.Request.Header.Get("Authorization"))
	user, err := services.GetUserByUsername(claims.Subject)
	if err != nil {
		this.CustomAbort(404, err.Error())
	}

	if ( ( user.HasRole("Admin") || user.HasRole("Canton") ) == false ) {
		this.CustomAbort(401, "Unauthorized")
	}

	request := services.GetRequestById(r.Id, true)

	apaymentTransfer := &models.APaymentTokenTransfer{
		From:user.EtherumAddress,
		To: request.User.EtherumAddress,
	}
	if ( len(request.Payments) == 0 ) {
		beego.Debug("make first payment")
		amount, err := services.GetFirstPaymentAmount(request)
		beego.Info("GetFirstPaymentAmount: ", amount)
		if (err != nil) {
			beego.Error("Error while getting first payment amount. ", err)
			this.CustomAbort(500, err.Error())
		}
		amount.Div(amount, big.NewInt(2)) // 50% of the amount

		apaymentTransfer.Amount = amount
		apaymentTransfer.Message = "First Payment"
		err = services.Transfer(apaymentTransfer, request.Address)
		if err != nil {
			beego.Debug("Error while transfer", err)
			this.CustomAbort(500, err.Error())
		}
	} else if ( len(request.Payments) == 1 ) {
		beego.Debug("make second payment")
		//services.AddPayment(request, common.HexToAddress(user.Address), big.NewInt(333))
	} else if ( len(request.Payments) == 2 ) {
		beego.Debug("make third payment")
	}

	//request = services.GetRequestById(r.Id)
	this.Data["json"] = request
	this.ServeJSON()
}<|endoftext|>


<commit_before>package parser

import (
    "bufio"
    "strings"
)

type functionScanner struct{
    items []string
}

func (f *functionScanner) match(line string) bool {
    if(len(line) >= 6){
        //todo: check for uppercase rune.
        if(line[0:5] == "func "){
            return true
        }
    }
    return false
}

func (f *functionScanner) scan(scanner *bufio.Scanner) {
    line := scanner.Text()
    index := strings.Index(line,"{")
    if(index != -1){
        f.items = append(f.items,line[5:index])
    }
}<commit_msg>forgot :)<commit_after>package parser

import (
    "bufio"
    "strings"
    "unicode"
    "regexp"
)

type functionScanner struct{
    items []string
}

func (f *functionScanner) match(line string) bool {
    if(len(line) >= 6){
        //todo: check for uppercase rune.
        if(line[0:5] == "func "){
            if(strings.IsUpperCase(line[6:7])
            return true
        }
    }
    return false
}

func (f *functionScanner) scan(scanner *bufio.Scanner) {
    line := scanner.Text()
    index := strings.Index(line,"{")
    if(index != -1){
        f.items = append(f.items,line[5:index])
    }
}

func extractFuncName(string line){
    //https://golang.org/ref/spec#Function_declarations
    //https://golang.org/ref/spec#Method_declarations
    //https://play.golang.org/p/7ccWVkM2kc
    //check if it is a method or function.
	reFunc := `(^func\b)`
	reReceiver := `(\([^\)]+\))?`
	reMethodName := `(\b\w+\b)`
	reParameters := `(\([^\)]*\))`
	reResult := `((?:\([^\)]+\))?|(?:\w+)?)`
	reWhiteSpace := `\s*`

    re := regexp.MustCompile(reFunc+reWhiteSpace+reReceiver+reWhiteSpace+reMethodName+reParameters+reWhiteSpace+reResult)

    //TODO:
    extract methodName
}<|endoftext|>


<commit_before>package util

/*
 Copyright 2017 - 2020 Crunchy Data Solutions, Inc.
 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.
*/

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"strconv"
	"time"

	crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
	"github.com/crunchydata/postgres-operator/kubeapi"
	jsonpatch "github.com/evanphx/json-patch"
	log "github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/validation"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const letterBytes = "abcdefghijklmnopqrstuvwxyz"

func init() {
	rand.Seed(time.Now().UnixNano())

}

// CreateSecContext will generate the JSON security context fragment
// for a storage type
func CreateSecContext(fsGroup string, suppGroup string) string {

	var sc bytes.Buffer
	var fsgroup = false
	var supp = false

	if fsGroup != "" {
		fsgroup = true
	}
	if suppGroup != "" {
		supp = true
	}
	if fsgroup || supp {
		sc.WriteString("\"securityContext\": {\n")
	}
	if fsgroup {
		sc.WriteString("\t \"fsGroup\": " + fsGroup)
		if fsgroup && supp {
			sc.WriteString(",")
		}
		sc.WriteString("\n")
	}

	if supp {
		sc.WriteString("\t \"supplementalGroups\": [" + suppGroup + "]\n")
	}

	//closing of securityContext
	if fsgroup || supp {
		sc.WriteString("},")
	}

	return sc.String()
}

// ThingSpec is a json patch structure
type ThingSpec struct {
	Op    string `json:"op"`
	Path  string `json:"path"`
	Value string `json:"value"`
}

// Patch will patch a particular resource
func Patch(restclient *rest.RESTClient, path string, value string, resource string, name string, namespace string) error {

	things := make([]ThingSpec, 1)
	things[0].Op = "replace"
	things[0].Path = path
	things[0].Value = value

	patchBytes, err4 := json.Marshal(things)
	if err4 != nil {
		log.Error("error in converting patch " + err4.Error())
	}
	log.Debug(string(patchBytes))

	_, err6 := restclient.Patch(types.JSONPatchType).
		Namespace(namespace).
		Resource(resource).
		Name(name).
		Body(patchBytes).
		Do().
		Get()

	return err6

}

// DrainDeployment will drain a deployment to 0 pods
func DrainDeployment(clientset *kubernetes.Clientset, name string, namespace string) error {

	var err error
	var patchBytes []byte

	things := make([]ThingSpec, 1)
	things[0].Op = "replace"
	things[0].Path = "/spec/replicas"
	things[0].Value = "0"

	patchBytes, err = json.Marshal(things)
	if err != nil {
		log.Error("error in converting patch " + err.Error())
	}
	log.Debug(string(patchBytes))

	_, err = clientset.AppsV1().Deployments(namespace).Patch(name, types.JSONPatchType, patchBytes, "")
	if err != nil {
		log.Error("error patching deployment " + err.Error())
	}

	return err

}

// CreatePVCSnippet generates the PVC json snippet
func CreatePVCSnippet(storageType string, PVCName string) string {

	var sc bytes.Buffer

	if storageType != "emptydir" {
		sc.WriteString("\"persistentVolumeClaim\": {\n")
		sc.WriteString("\t \"claimName\": \"" + PVCName + "\"")
		sc.WriteString("\n")
	} else {
		sc.WriteString("\"emptyDir\": {")
		sc.WriteString("\n")
	}

	sc.WriteString("}")

	return sc.String()
}

// CreateBackupPVCSnippet generates the PVC definition fragment
func CreateBackupPVCSnippet(backupPVCName string) string {

	var sc bytes.Buffer

	if backupPVCName != "" {
		sc.WriteString("\"persistentVolumeClaim\": {\n")
		sc.WriteString("\t \"claimName\": \"" + backupPVCName + "\"")
		sc.WriteString("\n")
	} else {
		sc.WriteString("\"emptyDir\": {")
		sc.WriteString("\n")
	}

	sc.WriteString("}")

	return sc.String()
}

// ScaleDeployment will increase the number of pods in a deployment
func ScaleDeployment(clientset *kubernetes.Clientset, deploymentName, namespace string, replicaCount int) error {
	var err error

	things := make([]ThingSpec, 1)
	things[0].Op = "replace"
	things[0].Path = "/spec/replicas"
	things[0].Value = strconv.Itoa(replicaCount)

	var patchBytes []byte
	patchBytes, err = json.Marshal(things)
	if err != nil {
		log.Error("error in converting patch " + err.Error())
		return err
	}
	log.Debug(string(patchBytes))

	_, err = clientset.AppsV1().Deployments(namespace).Patch(deploymentName, types.JSONPatchType, patchBytes)
	if err != nil {
		log.Error("error creating primary Deployment " + err.Error())
		return err
	}
	log.Debug("replica count patch succeeded")
	return err
}

// GetLabels ...
func GetLabels(name, clustername string, replica bool) string {
	var output string
	if replica {
		output += fmt.Sprintf("\"primary\": \"%s\",\n", "false")
	}
	output += fmt.Sprintf("\"name\": \"%s\",\n", name)
	output += fmt.Sprintf("\"pg-cluster\": \"%s\"\n", clustername)
	return output
}

// PatchClusterCRD patches the pgcluster CRD
func PatchClusterCRD(restclient *rest.RESTClient, labelMap map[string]string, oldCrd *crv1.Pgcluster, namespace string) error {

	oldData, err := json.Marshal(oldCrd)
	if err != nil {
		return err
	}

	if oldCrd.ObjectMeta.Labels == nil {
		oldCrd.ObjectMeta.Labels = make(map[string]string)
	}
	for k, v := range labelMap {
		if len(validation.IsQualifiedName(k)) == 0 && len(validation.IsValidLabelValue(v)) == 0 {
			oldCrd.ObjectMeta.Labels[k] = v
		} else {
			log.Debugf("user label %s:%s does not meet Kubernetes label requirements and will not be used to label "+
				"pgcluster %s", k, v, oldCrd.Spec.Name)
		}
	}

	var newData, patchBytes []byte
	newData, err = json.Marshal(oldCrd)
	if err != nil {
		return err
	}
	patchBytes, err = jsonpatch.CreateMergePatch(oldData, newData)
	if err != nil {
		return err
	}

	log.Debug(string(patchBytes))

	_, err6 := restclient.Patch(types.MergePatchType).
		Namespace(namespace).
		Resource(crv1.PgclusterResourcePlural).
		Name(oldCrd.Spec.Name).
		Body(patchBytes).
		Do().
		Get()

	return err6

}

// GetSecretPassword ...
func GetSecretPassword(clientset *kubernetes.Clientset, db, suffix, Namespace string) (string, error) {

	var err error

	selector := "pg-cluster=" + db
	secrets, err := kubeapi.GetSecrets(clientset, selector, Namespace)
	if err != nil {
		return "", err
	}

	log.Debugf("secrets for %s", db)
	secretName := db + suffix
	for _, s := range secrets.Items {
		log.Debugf("secret : %s", s.ObjectMeta.Name)
		if s.ObjectMeta.Name == secretName {
			log.Debug("pgprimary password found")
			return string(s.Data["password"][:]), err
		}
	}

	log.Error("primary secret not found for " + db)
	return "", errors.New("primary secret not found for " + db)

}

// RandStringBytesRmndr ...
func RandStringBytesRmndr(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
	}
	return string(b)
}

// CreateBackrestPVCSnippet
func CreateBackrestPVCSnippet(backRestPVCName string) string {

	var sc bytes.Buffer

	if backRestPVCName != "" {
		sc.WriteString("\"persistentVolumeClaim\": {\n")
		sc.WriteString("\t \"claimName\": \"" + backRestPVCName + "\"")
		sc.WriteString("\n")
	} else {
		sc.WriteString("\"emptyDir\": {")
		sc.WriteString("\"medium\": \"Memory\"")
		sc.WriteString("\n")
	}

	sc.WriteString("}")

	return sc.String()
}

// Generates an Md5Hash
func GetMD5HashForAuthFile(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// NewClient gets a REST connection to Kube
func NewClient(cfg *rest.Config) (*rest.RESTClient, *runtime.Scheme, error) {
	scheme := runtime.NewScheme()
	if err := crv1.AddToScheme(scheme); err != nil {
		return nil, nil, err
	}

	config := *cfg
	config.GroupVersion = &crv1.SchemeGroupVersion
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)}

	client, err := rest.RESTClientFor(&config)
	if err != nil {
		return nil, nil, err
	}

	return client, scheme, nil
}

// IsStringOneOf tests to see string testVal is included in the list
// of strings provided using acceptedVals
func IsStringOneOf(testVal string, acceptedVals ...string) bool {
	isOneOf := false
	for _, val := range acceptedVals {
		if testVal == val {
			isOneOf = true
			break
		}
	}
	return isOneOf
}
<commit_msg>Remove dead code<commit_after>package util

/*
 Copyright 2017 - 2020 Crunchy Data Solutions, Inc.
 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.
*/

import (
	"bytes"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/rand"
	"time"

	crv1 "github.com/crunchydata/postgres-operator/apis/cr/v1"
	"github.com/crunchydata/postgres-operator/kubeapi"
	jsonpatch "github.com/evanphx/json-patch"
	log "github.com/sirupsen/logrus"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/validation"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

const letterBytes = "abcdefghijklmnopqrstuvwxyz"

func init() {
	rand.Seed(time.Now().UnixNano())

}

// CreateSecContext will generate the JSON security context fragment
// for a storage type
func CreateSecContext(fsGroup string, suppGroup string) string {

	var sc bytes.Buffer
	var fsgroup = false
	var supp = false

	if fsGroup != "" {
		fsgroup = true
	}
	if suppGroup != "" {
		supp = true
	}
	if fsgroup || supp {
		sc.WriteString("\"securityContext\": {\n")
	}
	if fsgroup {
		sc.WriteString("\t \"fsGroup\": " + fsGroup)
		if fsgroup && supp {
			sc.WriteString(",")
		}
		sc.WriteString("\n")
	}

	if supp {
		sc.WriteString("\t \"supplementalGroups\": [" + suppGroup + "]\n")
	}

	//closing of securityContext
	if fsgroup || supp {
		sc.WriteString("},")
	}

	return sc.String()
}

// ThingSpec is a json patch structure
type ThingSpec struct {
	Op    string `json:"op"`
	Path  string `json:"path"`
	Value string `json:"value"`
}

// Patch will patch a particular resource
func Patch(restclient *rest.RESTClient, path string, value string, resource string, name string, namespace string) error {

	things := make([]ThingSpec, 1)
	things[0].Op = "replace"
	things[0].Path = path
	things[0].Value = value

	patchBytes, err4 := json.Marshal(things)
	if err4 != nil {
		log.Error("error in converting patch " + err4.Error())
	}
	log.Debug(string(patchBytes))

	_, err6 := restclient.Patch(types.JSONPatchType).
		Namespace(namespace).
		Resource(resource).
		Name(name).
		Body(patchBytes).
		Do().
		Get()

	return err6

}

// CreatePVCSnippet generates the PVC json snippet
func CreatePVCSnippet(storageType string, PVCName string) string {

	var sc bytes.Buffer

	if storageType != "emptydir" {
		sc.WriteString("\"persistentVolumeClaim\": {\n")
		sc.WriteString("\t \"claimName\": \"" + PVCName + "\"")
		sc.WriteString("\n")
	} else {
		sc.WriteString("\"emptyDir\": {")
		sc.WriteString("\n")
	}

	sc.WriteString("}")

	return sc.String()
}

// CreateBackupPVCSnippet generates the PVC definition fragment
func CreateBackupPVCSnippet(backupPVCName string) string {

	var sc bytes.Buffer

	if backupPVCName != "" {
		sc.WriteString("\"persistentVolumeClaim\": {\n")
		sc.WriteString("\t \"claimName\": \"" + backupPVCName + "\"")
		sc.WriteString("\n")
	} else {
		sc.WriteString("\"emptyDir\": {")
		sc.WriteString("\n")
	}

	sc.WriteString("}")

	return sc.String()
}

// GetLabels ...
func GetLabels(name, clustername string, replica bool) string {
	var output string
	if replica {
		output += fmt.Sprintf("\"primary\": \"%s\",\n", "false")
	}
	output += fmt.Sprintf("\"name\": \"%s\",\n", name)
	output += fmt.Sprintf("\"pg-cluster\": \"%s\"\n", clustername)
	return output
}

// PatchClusterCRD patches the pgcluster CRD
func PatchClusterCRD(restclient *rest.RESTClient, labelMap map[string]string, oldCrd *crv1.Pgcluster, namespace string) error {

	oldData, err := json.Marshal(oldCrd)
	if err != nil {
		return err
	}

	if oldCrd.ObjectMeta.Labels == nil {
		oldCrd.ObjectMeta.Labels = make(map[string]string)
	}
	for k, v := range labelMap {
		if len(validation.IsQualifiedName(k)) == 0 && len(validation.IsValidLabelValue(v)) == 0 {
			oldCrd.ObjectMeta.Labels[k] = v
		} else {
			log.Debugf("user label %s:%s does not meet Kubernetes label requirements and will not be used to label "+
				"pgcluster %s", k, v, oldCrd.Spec.Name)
		}
	}

	var newData, patchBytes []byte
	newData, err = json.Marshal(oldCrd)
	if err != nil {
		return err
	}
	patchBytes, err = jsonpatch.CreateMergePatch(oldData, newData)
	if err != nil {
		return err
	}

	log.Debug(string(patchBytes))

	_, err6 := restclient.Patch(types.MergePatchType).
		Namespace(namespace).
		Resource(crv1.PgclusterResourcePlural).
		Name(oldCrd.Spec.Name).
		Body(patchBytes).
		Do().
		Get()

	return err6

}

// GetSecretPassword ...
func GetSecretPassword(clientset *kubernetes.Clientset, db, suffix, Namespace string) (string, error) {

	var err error

	selector := "pg-cluster=" + db
	secrets, err := kubeapi.GetSecrets(clientset, selector, Namespace)
	if err != nil {
		return "", err
	}

	log.Debugf("secrets for %s", db)
	secretName := db + suffix
	for _, s := range secrets.Items {
		log.Debugf("secret : %s", s.ObjectMeta.Name)
		if s.ObjectMeta.Name == secretName {
			log.Debug("pgprimary password found")
			return string(s.Data["password"][:]), err
		}
	}

	log.Error("primary secret not found for " + db)
	return "", errors.New("primary secret not found for " + db)

}

// RandStringBytesRmndr ...
func RandStringBytesRmndr(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
	}
	return string(b)
}

// CreateBackrestPVCSnippet
func CreateBackrestPVCSnippet(backRestPVCName string) string {

	var sc bytes.Buffer

	if backRestPVCName != "" {
		sc.WriteString("\"persistentVolumeClaim\": {\n")
		sc.WriteString("\t \"claimName\": \"" + backRestPVCName + "\"")
		sc.WriteString("\n")
	} else {
		sc.WriteString("\"emptyDir\": {")
		sc.WriteString("\"medium\": \"Memory\"")
		sc.WriteString("\n")
	}

	sc.WriteString("}")

	return sc.String()
}

// Generates an Md5Hash
func GetMD5HashForAuthFile(text string) string {
	hasher := md5.New()
	hasher.Write([]byte(text))
	return hex.EncodeToString(hasher.Sum(nil))
}

// NewClient gets a REST connection to Kube
func NewClient(cfg *rest.Config) (*rest.RESTClient, *runtime.Scheme, error) {
	scheme := runtime.NewScheme()
	if err := crv1.AddToScheme(scheme); err != nil {
		return nil, nil, err
	}

	config := *cfg
	config.GroupVersion = &crv1.SchemeGroupVersion
	config.APIPath = "/apis"
	config.ContentType = runtime.ContentTypeJSON
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: serializer.NewCodecFactory(scheme)}

	client, err := rest.RESTClientFor(&config)
	if err != nil {
		return nil, nil, err
	}

	return client, scheme, nil
}

// IsStringOneOf tests to see string testVal is included in the list
// of strings provided using acceptedVals
func IsStringOneOf(testVal string, acceptedVals ...string) bool {
	isOneOf := false
	for _, val := range acceptedVals {
		if testVal == val {
			isOneOf = true
			break
		}
	}
	return isOneOf
}
<|endoftext|>


<commit_before>package conntrack

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/sirupsen/logrus"
)

type InstrumentedConn struct {
	net.Conn
	Role         string
	OutboundHost string

	tracker *Tracker

	Start        time.Time
	LastActivity *int64 // Unix nano

	BytesIn  *uint64
	BytesOut *uint64

	sync.Mutex

	closed     bool
	CloseError error
}

func (t *Tracker) NewInstrumentedConn(conn net.Conn, role, outboundHost string) *InstrumentedConn {
	now := time.Now().UnixNano()
	bytesIn := uint64(0)
	bytesOut := uint64(0)

	ic := &InstrumentedConn{
		Conn:         conn,
		Role:         role,
		tracker:      t,
		Start:        time.Now(),
		LastActivity: &now,
		BytesIn:      &bytesIn,
		BytesOut:     &bytesOut,
	}

	ic.tracker.Store(ic, nil)
	ic.tracker.Wg.Add(1)

	return ic
}

func (ic *InstrumentedConn) Close() error {
	ic.Lock()
	defer ic.Unlock()

	if ic.closed {
		return ic.CloseError
	}

	ic.closed = true
	ic.tracker.Delete(ic)

	end := time.Now()
	duration := end.Sub(ic.Start).Seconds()

	tags := []string{
		fmt.Sprintf("role:%s", ic.Role),
	}

	ic.tracker.statsc.Incr("cn.close", tags, 1)
	ic.tracker.statsc.Histogram("cn.duration", duration, tags, 1)
	ic.tracker.statsc.Histogram("cn.bytes_in", float64(*ic.BytesIn), tags, 1)
	ic.tracker.statsc.Histogram("cn.bytes_out", float64(*ic.BytesOut), tags, 1)

	// Track when we terminate active connections during a shutdown
	idle := true
	if ic.tracker.ShuttingDown.Load() == true {
		idle = ic.Idle()
		if !idle {
			ic.tracker.statsc.Incr("cn.active_at_termination", tags, 1)
		}
	}

	ic.tracker.Log.WithFields(logrus.Fields{
		"idle":        idle,
		"bytes_in":    ic.BytesIn,
		"bytes_out":   ic.BytesOut,
		"role":        ic.Role,
		"req_host":    ic.OutboundHost,
		"remote_addr": ic.Conn.RemoteAddr(),
		"start_time":  ic.Start.UTC(),
		"end_time":    end.UTC(),
		"duration":    duration,
	}).Info("CANONICAL-PROXY-CN-CLOSE")

	ic.tracker.Wg.Done()

	ic.CloseError = ic.Conn.Close()
	return ic.CloseError
}

func (ic *InstrumentedConn) Read(b []byte) (int, error) {
	atomic.StoreInt64(ic.LastActivity, time.Now().UnixNano())

	n, err := ic.Conn.Read(b)
	atomic.AddUint64(ic.BytesIn, uint64(n))

	return n, err
}

func (ic *InstrumentedConn) Write(b []byte) (int, error) {
	atomic.StoreInt64(ic.LastActivity, time.Now().UnixNano())

	n, err := ic.Conn.Write(b)
	atomic.AddUint64(ic.BytesOut, uint64(n))

	return n, err
}

// Idle returns true when the connection's last activity occured before the
// configured idle threshold.
//
// Idle should be called with the connection's lock held.
func (ic *InstrumentedConn) Idle() bool {
	if time.Since(time.Unix(0, *ic.LastActivity)) > ic.tracker.IdleThreshold {
		return true
	}
	return false
}

func (ic *InstrumentedConn) Stats() *InstrumentedConnStats {
	ic.Lock()
	defer ic.Unlock()

	return &InstrumentedConnStats{
		Id:                       fmt.Sprintf("%d", &ic),
		Role:                     ic.Role,
		Rhost:                    ic.OutboundHost,
		Created:                  ic.Start,
		BytesIn:                  *ic.BytesIn,
		BytesOut:                 *ic.BytesOut,
		SecondsSinceLastActivity: time.Now().Sub(time.Unix(0, *ic.LastActivity)).Seconds(),
	}
}

func (ic *InstrumentedConn) JsonStats() ([]byte, error) {
	return json.Marshal(ic.Stats())
}
<commit_msg>conntrack: store OutboundHost in InstrumentedConn<commit_after>package conntrack

import (
	"encoding/json"
	"fmt"
	"net"
	"sync"
	"sync/atomic"
	"time"

	"github.com/sirupsen/logrus"
)

type InstrumentedConn struct {
	net.Conn
	Role         string
	OutboundHost string

	tracker *Tracker

	Start        time.Time
	LastActivity *int64 // Unix nano

	BytesIn  *uint64
	BytesOut *uint64

	sync.Mutex

	closed     bool
	CloseError error
}

func (t *Tracker) NewInstrumentedConn(conn net.Conn, role, outboundHost string) *InstrumentedConn {
	now := time.Now().UnixNano()
	bytesIn := uint64(0)
	bytesOut := uint64(0)

	ic := &InstrumentedConn{
		Conn:         conn,
		Role:         role,
		OutboundHost: outboundHost,
		tracker:      t,
		Start:        time.Now(),
		LastActivity: &now,
		BytesIn:      &bytesIn,
		BytesOut:     &bytesOut,
	}

	ic.tracker.Store(ic, nil)
	ic.tracker.Wg.Add(1)

	return ic
}

func (ic *InstrumentedConn) Close() error {
	ic.Lock()
	defer ic.Unlock()

	if ic.closed {
		return ic.CloseError
	}

	ic.closed = true
	ic.tracker.Delete(ic)

	end := time.Now()
	duration := end.Sub(ic.Start).Seconds()

	tags := []string{
		fmt.Sprintf("role:%s", ic.Role),
	}

	ic.tracker.statsc.Incr("cn.close", tags, 1)
	ic.tracker.statsc.Histogram("cn.duration", duration, tags, 1)
	ic.tracker.statsc.Histogram("cn.bytes_in", float64(*ic.BytesIn), tags, 1)
	ic.tracker.statsc.Histogram("cn.bytes_out", float64(*ic.BytesOut), tags, 1)

	// Track when we terminate active connections during a shutdown
	idle := true
	if ic.tracker.ShuttingDown.Load() == true {
		idle = ic.Idle()
		if !idle {
			ic.tracker.statsc.Incr("cn.active_at_termination", tags, 1)
		}
	}

	ic.tracker.Log.WithFields(logrus.Fields{
		"idle":        idle,
		"bytes_in":    ic.BytesIn,
		"bytes_out":   ic.BytesOut,
		"role":        ic.Role,
		"req_host":    ic.OutboundHost,
		"remote_addr": ic.Conn.RemoteAddr(),
		"start_time":  ic.Start.UTC(),
		"end_time":    end.UTC(),
		"duration":    duration,
	}).Info("CANONICAL-PROXY-CN-CLOSE")

	ic.tracker.Wg.Done()

	ic.CloseError = ic.Conn.Close()
	return ic.CloseError
}

func (ic *InstrumentedConn) Read(b []byte) (int, error) {
	atomic.StoreInt64(ic.LastActivity, time.Now().UnixNano())

	n, err := ic.Conn.Read(b)
	atomic.AddUint64(ic.BytesIn, uint64(n))

	return n, err
}

func (ic *InstrumentedConn) Write(b []byte) (int, error) {
	atomic.StoreInt64(ic.LastActivity, time.Now().UnixNano())

	n, err := ic.Conn.Write(b)
	atomic.AddUint64(ic.BytesOut, uint64(n))

	return n, err
}

// Idle returns true when the connection's last activity occured before the
// configured idle threshold.
//
// Idle should be called with the connection's lock held.
func (ic *InstrumentedConn) Idle() bool {
	if time.Since(time.Unix(0, *ic.LastActivity)) > ic.tracker.IdleThreshold {
		return true
	}
	return false
}

func (ic *InstrumentedConn) Stats() *InstrumentedConnStats {
	ic.Lock()
	defer ic.Unlock()

	return &InstrumentedConnStats{
		Id:                       fmt.Sprintf("%d", &ic),
		Role:                     ic.Role,
		Rhost:                    ic.OutboundHost,
		Created:                  ic.Start,
		BytesIn:                  *ic.BytesIn,
		BytesOut:                 *ic.BytesOut,
		SecondsSinceLastActivity: time.Now().Sub(time.Unix(0, *ic.LastActivity)).Seconds(),
	}
}

func (ic *InstrumentedConn) JsonStats() ([]byte, error) {
	return json.Marshal(ic.Stats())
}
<|endoftext|>


<commit_before>package controllers

import (
	"fmt"
	"log"
	"net/http"

	"github.com/nuveo/prest/adapters/postgres"
	"github.com/nuveo/prest/statements"
)

// GetSchemas list all (or filter) schemas
func GetSchemas(w http.ResponseWriter, r *http.Request) {
	requestWhere := postgres.WhereByRequest(r)
	sqlSchemas := statements.Schemas
	if requestWhere != "" {
		sqlSchemas = fmt.Sprint(
			statements.SchemasSelect,
			requestWhere,
			statements.SchemasOrderBy)
	}
	object, err := postgres.Query(sqlSchemas)
	if err != nil {
		log.Println(err)
	}

	w.Write(object)
}
<commit_msg>fix schemas query when has where clause<commit_after>package controllers

import (
	"fmt"
	"log"
	"net/http"

	"github.com/nuveo/prest/adapters/postgres"
	"github.com/nuveo/prest/statements"
)

// GetSchemas list all (or filter) schemas
func GetSchemas(w http.ResponseWriter, r *http.Request) {
	requestWhere := postgres.WhereByRequest(r)
	sqlSchemas := statements.Schemas
	if requestWhere != "" {
		sqlSchemas = fmt.Sprint(
			statements.SchemasSelect,
			"WHERE",
			requestWhere,
			statements.SchemasOrderBy)
	}
	object, err := postgres.Query(sqlSchemas)
	if err != nil {
		log.Println(err)
	}

	w.Write(object)
}
<|endoftext|>


<commit_before>package logmetrics

import (
	"fmt"
	"log"
	"net"
	"time"
)

type keyPushStats struct {
	key_pushed     int64
	byte_pushed    int64
	last_report    time.Time
	hostname       string
	interval       int
	channel_number int
}

func (f *keyPushStats) inc(data_written int) {
	f.key_pushed++
	f.byte_pushed += int64(data_written)
}

func (f *keyPushStats) getLine() []string {
	t := time.Now()

	f.last_report = t

	line := make([]string, 2)
	line[0] = fmt.Sprintf("logmetrics_collector.pusher.key_sent %d %d host=%s pusher_number=%d", t.Unix(), f.key_pushed, f.hostname, f.pusher_number)
	line[1] = fmt.Sprintf("logmetrics_collector.pusher.byte_sent %d %d host=%s pusher_number=%d", t.Unix(), f.byte_pushed, f.hostname, f.pusher_number)

	return line
}

func (f *keyPushStats) isTimeForStats() bool {
	return time.Now().Sub(f.last_report) > time.Duration(f.interval)*time.Second
}

func writeLine(config *Config, doNotSend bool, conn net.Conn, line string) (int, net.Conn) {
	if config.pushType == "tsd" {
		line = ("put " + line + "\n")
	} else {
		line = line + "\n"
	}

	byte_line := []byte(line)
	byte_written := len(byte_line)

	var err error
	if doNotSend {
		fmt.Print(line)
	} else {
		for {
			//Reconnect if needed
			if conn == nil {
				target := config.GetTsdTarget()
				log.Printf("Reconnecting to %s", target)

				if conn, err = net.Dial(config.pushProto, target); err != nil {
					log.Printf("Unable to reconnect: %s", err)
					time.Sleep(time.Duration(config.pushWait) * time.Second)
				}
			}

			if conn != nil {
				_, err = conn.Write(byte_line)

				if err != nil {
					log.Printf("Error writting data: %s", err)
					conn.Close()
					conn = nil
					time.Sleep(time.Duration(config.pushWait) * time.Second)
				} else {
					break
				}
			}

		}
	}

	return byte_written, conn
}

func StartTsdPushers(config *Config, tsd_pushers []chan []string, doNotSend bool) {
	if config.pushPort == 0 {
		return
	}

	hostname := getHostname()

	for i, _ := range tsd_pushers {
		channel_number := i

		log.Printf("TsdPusher[%d] started. Pushing keys to %s:%d over %s in %s format", channel_number, config.pushHost,
			config.pushPort, config.pushProto, config.pushType)

		tsd_push := tsd_pushers[channel_number]
		go func() {
			key_push_stats := keyPushStats{last_report: time.Now(), hostname: hostname, interval: config.stats_wait, channel_number: channel_number}

			//Check if TSD has something to say
			//if config.pushType == "tsd" {
			//	go func() {
			//		response_buffer := make([]byte, 1024)
			//		for {
			//			if conn != nil {
			//				if size, read_err := conn.Read(response_buffer); read_err != nil && read_err != io.EOF {
			//					log.Printf("Unable to read response: %s %+V", read_err, read_err)
			//				} else if size > 0 {
			//					log.Print(string(response_buffer))
			//				}
			//			}
			//		}
			//	}()
			//}

			var conn net.Conn
			for keys := range tsd_push {
				for _, line := range keys {
					var bytes_written int
					bytes_written, conn = writeLine(config, doNotSend, conn, line)

					key_push_stats.inc(bytes_written)

					//Stats on key pushed, limit checks with modulo (now() is a syscall)
					if (key_push_stats.key_pushed%10000) == 0 && key_push_stats.isTimeForStats() {
						for _, local_line := range key_push_stats.getLine() {
							bytes_written, conn = writeLine(config, doNotSend, conn, local_line)
							key_push_stats.inc(bytes_written)
						}
					}
				}
			}

		}()
	}
}
<commit_msg>Fix on addition of push tag for internal stats.<commit_after>package logmetrics

import (
	"fmt"
	"log"
	"net"
	"time"
)

type keyPushStats struct {
	key_pushed    int64
	byte_pushed   int64
	last_report   time.Time
	hostname      string
	interval      int
	pusher_number int
}

func (f *keyPushStats) inc(data_written int) {
	f.key_pushed++
	f.byte_pushed += int64(data_written)
}

func (f *keyPushStats) getLine() []string {
	t := time.Now()

	f.last_report = t

	line := make([]string, 2)
	line[0] = fmt.Sprintf("logmetrics_collector.pusher.key_sent %d %d host=%s pusher_number=%d", t.Unix(), f.key_pushed, f.hostname, f.pusher_number)
	line[1] = fmt.Sprintf("logmetrics_collector.pusher.byte_sent %d %d host=%s pusher_number=%d", t.Unix(), f.byte_pushed, f.hostname, f.pusher_number)

	return line
}

func (f *keyPushStats) isTimeForStats() bool {
	return time.Now().Sub(f.last_report) > time.Duration(f.interval)*time.Second
}

func writeLine(config *Config, doNotSend bool, conn net.Conn, line string) (int, net.Conn) {
	if config.pushType == "tsd" {
		line = ("put " + line + "\n")
	} else {
		line = line + "\n"
	}

	byte_line := []byte(line)
	byte_written := len(byte_line)

	var err error
	if doNotSend {
		fmt.Print(line)
	} else {
		for {
			//Reconnect if needed
			if conn == nil {
				target := config.GetTsdTarget()
				log.Printf("Reconnecting to %s", target)

				if conn, err = net.Dial(config.pushProto, target); err != nil {
					log.Printf("Unable to reconnect: %s", err)
					time.Sleep(time.Duration(config.pushWait) * time.Second)
				}
			}

			if conn != nil {
				_, err = conn.Write(byte_line)

				if err != nil {
					log.Printf("Error writting data: %s", err)
					conn.Close()
					conn = nil
					time.Sleep(time.Duration(config.pushWait) * time.Second)
				} else {
					break
				}
			}

		}
	}

	return byte_written, conn
}

func StartTsdPushers(config *Config, tsd_pushers []chan []string, doNotSend bool) {
	if config.pushPort == 0 {
		return
	}

	hostname := getHostname()

	for i, _ := range tsd_pushers {
		channel_number := i

		log.Printf("TsdPusher[%d] started. Pushing keys to %s:%d over %s in %s format", channel_number, config.pushHost,
			config.pushPort, config.pushProto, config.pushType)

		tsd_push := tsd_pushers[channel_number]
		go func() {
			key_push_stats := keyPushStats{last_report: time.Now(), hostname: hostname, interval: config.stats_wait, pusher_number: channel_number}

			//Check if TSD has something to say
			//if config.pushType == "tsd" {
			//	go func() {
			//		response_buffer := make([]byte, 1024)
			//		for {
			//			if conn != nil {
			//				if size, read_err := conn.Read(response_buffer); read_err != nil && read_err != io.EOF {
			//					log.Printf("Unable to read response: %s %+V", read_err, read_err)
			//				} else if size > 0 {
			//					log.Print(string(response_buffer))
			//				}
			//			}
			//		}
			//	}()
			//}

			var conn net.Conn
			for keys := range tsd_push {
				for _, line := range keys {
					var bytes_written int
					bytes_written, conn = writeLine(config, doNotSend, conn, line)

					key_push_stats.inc(bytes_written)

					//Stats on key pushed, limit checks with modulo (now() is a syscall)
					if (key_push_stats.key_pushed%10000) == 0 && key_push_stats.isTimeForStats() {
						for _, local_line := range key_push_stats.getLine() {
							bytes_written, conn = writeLine(config, doNotSend, conn, local_line)
							key_push_stats.inc(bytes_written)
						}
					}
				}
			}

		}()
	}
}
<|endoftext|>