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

package math

import "math"

// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
// and came with this notice.  The go code is a simpler
// version of the original C.
//
// ====================================================
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
//
// Developed at SunPro, a Sun Microsystems, Inc. business.
// Permission to use, copy, modify, and distribute this
// software is freely granted, provided that this notice
// is preserved.
// ====================================================
//
// __ieee754_log(x)
// Return the logrithm of x
//
// Method :
//   1. Argument Reduction: find k and f such that
//			x = 2^k * (1+f),
//	   where  sqrt(2)/2 < 1+f < sqrt(2) .
//
//   2. Approximation of log(1+f).
//	Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
//		 = 2s + 2/3 s**3 + 2/5 s**5 + .....,
//	     	 = 2s + s*R
//      We use a special Reme algorithm on [0,0.1716] to generate
// 	a polynomial of degree 14 to approximate R The maximum error
//	of this polynomial approximation is bounded by 2**-58.45. In
//	other words,
//		        2      4      6      8      10      12      14
//	    R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s  +L6*s  +L7*s
//  	(the values of L1 to L7 are listed in the program)
//	and
//	    |      2          14          |     -58.45
//	    | L1*s +...+L7*s    -  R(z) | <= 2
//	    |                             |
//	Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
//	In order to guarantee error in log below 1ulp, we compute log
//	by
//		log(1+f) = f - s*(f - R)	(if f is not too large)
//		log(1+f) = f - (hfsq - s*(hfsq+R)).	(better accuracy)
//
//	3. Finally,  log(x) = k*Ln2 + log(1+f).
//			    = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo)))
//	   Here Ln2 is split into two floating point number:
//			Ln2_hi + Ln2_lo,
//	   where n*Ln2_hi is always exact for |n| < 2000.
//
// Special cases:
//	log(x) is NaN with signal if x < 0 (including -INF) ;
//	log(+INF) is +INF; log(0) is -INF with signal;
//	log(NaN) is that NaN with no signal.
//
// Accuracy:
//	according to an error analysis, the error is always less than
//	1 ulp (unit in the last place).
//
// Constants:
// The hexadecimal values are the intended ones for the following
// constants. The decimal values may be used, provided that the
// compiler will convert from decimal to binary accurately enough
// to produce the hexadecimal values shown.

// Log returns the natural logarithm of x.
//
// Special cases are:
//	Log(+Inf) = +Inf
//	Log(0) = -Inf
//	Log(x < 0) = NaN
//	Log(NaN) = NaN
func Log(x float64) float64 {
	const (
		Ln2Hi = 6.93147180369123816490e-01;	/* 3fe62e42 fee00000 */
		Ln2Lo = 1.90821492927058770002e-10;	/* 3dea39ef 35793c76 */
		L1 = 6.666666666666735130e-01;  /* 3FE55555 55555593 */
		L2 = 3.999999999940941908e-01;  /* 3FD99999 9997FA04 */
		L3 = 2.857142874366239149e-01;  /* 3FD24924 94229359 */
		L4 = 2.222219843214978396e-01;  /* 3FCC71C5 1D8E78AF */
		L5 = 1.818357216161805012e-01;  /* 3FC74664 96CB03DE */
		L6 = 1.531383769920937332e-01;  /* 3FC39A09 D078C69F */
		L7 = 1.479819860511658591e-01;  /* 3FC2F112 DF3E5244 */
	)

	// special cases
	switch {
	case IsNaN(x) || IsInf(x, 1):
		return x;
	case x < 0:
		return NaN();
	case x == 0:
		return Inf(-1);
	}

	// reduce
	f1, ki := Frexp(x);
	if f1 < Sqrt2/2 {
		f1 *= 2;
		ki--;
	}
	f := f1 - 1;
	k := float64(ki);

	// compute
	s := f/(2+f);
	s2 := s*s;
	s4 := s2*s2;
	t1 := s2*(L1 + s4*(L3 + s4*(L5 + s4*L7)));
	t2 := s4*(L2 + s4*(L4 + s4*L6));
	R :=  t1 + t2;
	hfsq := 0.5*f*f;
	return k*Ln2Hi - ((hfsq-(s*(hfsq+R)+k*Ln2Lo)) - f);
}

// Log10 returns the decimal logarthm of x.
// The special cases are the same as for Log.
func Log10(x float64) float64 {
	if x <= 0 {
		return NaN();
	}
	return Log(x) * (1/Ln10);
}

<commit_msg>fixed typo<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package math

import "math"

// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
// and came with this notice.  The go code is a simpler
// version of the original C.
//
// ====================================================
// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
//
// Developed at SunPro, a Sun Microsystems, Inc. business.
// Permission to use, copy, modify, and distribute this
// software is freely granted, provided that this notice
// is preserved.
// ====================================================
//
// __ieee754_log(x)
// Return the logrithm of x
//
// Method :
//   1. Argument Reduction: find k and f such that
//			x = 2^k * (1+f),
//	   where  sqrt(2)/2 < 1+f < sqrt(2) .
//
//   2. Approximation of log(1+f).
//	Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
//		 = 2s + 2/3 s**3 + 2/5 s**5 + .....,
//	     	 = 2s + s*R
//      We use a special Reme algorithm on [0,0.1716] to generate
// 	a polynomial of degree 14 to approximate R The maximum error
//	of this polynomial approximation is bounded by 2**-58.45. In
//	other words,
//		        2      4      6      8      10      12      14
//	    R(z) ~ L1*s +L2*s +L3*s +L4*s +L5*s  +L6*s  +L7*s
//  	(the values of L1 to L7 are listed in the program)
//	and
//	    |      2          14          |     -58.45
//	    | L1*s +...+L7*s    -  R(z) | <= 2
//	    |                             |
//	Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
//	In order to guarantee error in log below 1ulp, we compute log
//	by
//		log(1+f) = f - s*(f - R)	(if f is not too large)
//		log(1+f) = f - (hfsq - s*(hfsq+R)).	(better accuracy)
//
//	3. Finally,  log(x) = k*Ln2 + log(1+f).
//			    = k*Ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*Ln2_lo)))
//	   Here Ln2 is split into two floating point number:
//			Ln2_hi + Ln2_lo,
//	   where n*Ln2_hi is always exact for |n| < 2000.
//
// Special cases:
//	log(x) is NaN with signal if x < 0 (including -INF) ;
//	log(+INF) is +INF; log(0) is -INF with signal;
//	log(NaN) is that NaN with no signal.
//
// Accuracy:
//	according to an error analysis, the error is always less than
//	1 ulp (unit in the last place).
//
// Constants:
// The hexadecimal values are the intended ones for the following
// constants. The decimal values may be used, provided that the
// compiler will convert from decimal to binary accurately enough
// to produce the hexadecimal values shown.

// Log returns the natural logarithm of x.
//
// Special cases are:
//	Log(+Inf) = +Inf
//	Log(0) = -Inf
//	Log(x < 0) = NaN
//	Log(NaN) = NaN
func Log(x float64) float64 {
	const (
		Ln2Hi = 6.93147180369123816490e-01;	/* 3fe62e42 fee00000 */
		Ln2Lo = 1.90821492927058770002e-10;	/* 3dea39ef 35793c76 */
		L1 = 6.666666666666735130e-01;  /* 3FE55555 55555593 */
		L2 = 3.999999999940941908e-01;  /* 3FD99999 9997FA04 */
		L3 = 2.857142874366239149e-01;  /* 3FD24924 94229359 */
		L4 = 2.222219843214978396e-01;  /* 3FCC71C5 1D8E78AF */
		L5 = 1.818357216161805012e-01;  /* 3FC74664 96CB03DE */
		L6 = 1.531383769920937332e-01;  /* 3FC39A09 D078C69F */
		L7 = 1.479819860511658591e-01;  /* 3FC2F112 DF3E5244 */
	)

	// special cases
	switch {
	case IsNaN(x) || IsInf(x, 1):
		return x;
	case x < 0:
		return NaN();
	case x == 0:
		return Inf(-1);
	}

	// reduce
	f1, ki := Frexp(x);
	if f1 < Sqrt2/2 {
		f1 *= 2;
		ki--;
	}
	f := f1 - 1;
	k := float64(ki);

	// compute
	s := f/(2+f);
	s2 := s*s;
	s4 := s2*s2;
	t1 := s2*(L1 + s4*(L3 + s4*(L5 + s4*L7)));
	t2 := s4*(L2 + s4*(L4 + s4*L6));
	R :=  t1 + t2;
	hfsq := 0.5*f*f;
	return k*Ln2Hi - ((hfsq-(s*(hfsq+R)+k*Ln2Lo)) - f);
}

// Log10 returns the decimal logarithm of x.
// The special cases are the same as for Log.
func Log10(x float64) float64 {
	if x <= 0 {
		return NaN();
	}
	return Log(x) * (1/Ln10);
}

<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"path/filepath"
	"sort"
	"testing"

	"zombiezen.com/go/capnproto2"
	"zombiezen.com/go/capnproto2/internal/schema"
)

func readTestFile(name string) ([]byte, error) {
	path := filepath.Join("testdata", name)
	return ioutil.ReadFile(path)
}

func mustReadTestFile(t *testing.T, name string) []byte {
	data, err := readTestFile(name)
	if err != nil {
		t.Fatal(err)
	}
	return data
}

func mustReadGeneratorRequest(t *testing.T, name string) schema.CodeGeneratorRequest {
	data := mustReadTestFile(t, name)
	msg, err := capnp.Unmarshal(data)
	if err != nil {
		t.Fatalf("Unmarshaling %s: %v", name, err)
	}
	req, err := schema.ReadRootCodeGeneratorRequest(msg)
	if err != nil {
		t.Fatalf("Reading code generator request %s: %v", name, err)
	}
	return req
}

func TestGoCapnpNodeMap(t *testing.T) {
	req := mustReadGeneratorRequest(t, "go.capnp.out")
	nodes, err := buildNodeMap(req)
	if err != nil {
		t.Error("buildNodeMap:", err)
	}
	want := []uint64{
		0xd12a1c51fedd6c88,
		0xbea97f1023792be0,
		0xe130b601260e44b5,
		0xc58ad6bd519f935e,
		0xa574b41924caefc7,
		0xc8768679ec52e012,
		0xfa10659ae02f2093,
		0xc2b96012172f8df1,
	}
	for _, k := range want {
		if nodes[k] == nil {
			t.Errorf("missing @%#x from node map", k)
		}
	}
}

func TestRemoteScope(t *testing.T) {
	type scopeTest struct {
		name        string
		constID     uint64
		initImports []importSpec

		remoteName string
		remoteNew  string
		imports    []importSpec
	}
	tests := []scopeTest{
		{
			name:       "same-file struct",
			constID:    0x84efedc75e99768d, // scopes.fooVar
			remoteName: "Foo",
			remoteNew:  "NewFoo",
		},
		{
			name:       "different file struct",
			constID:    0x836faf1834d91729, // scopes.otherFooVar
			remoteName: "otherscopes.Foo",
			remoteNew:  "otherscopes.NewFoo",
			imports: []importSpec{
				{name: "otherscopes", path: "zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes"},
			},
		},
		{
			name:       "same-file struct list",
			constID:    0xcda2680ec5c921e0, // scopes.fooListVar
			remoteName: "Foo_List",
			remoteNew:  "NewFoo_List",
		},
		{
			name:       "different file struct list",
			constID:    0x83e7e1b3cd1be338, // scopes.otherFooListVar
			remoteName: "otherscopes.Foo_List",
			remoteNew:  "otherscopes.NewFoo_List",
			imports: []importSpec{
				{name: "otherscopes", path: "zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes"},
			},
		},
		{
			name:       "built-in Int32 list",
			constID:    0xacf3d9917d0bb0f0, // scopes.intList
			remoteName: "capnp.Int32List",
			remoteNew:  "capnp.NewInt32List",
			imports: []importSpec{
				{name: "capnp", path: "zombiezen.com/go/capnproto2"},
			},
		},
	}
	req := mustReadGeneratorRequest(t, "scopes.capnp.out")
	nodes, err := buildNodeMap(req)
	if err != nil {
		t.Fatal("buildNodeMap:", err)
	}
	collect := func(test scopeTest) (g *generator, typ schema.Type, from *node, ok bool) {
		g = newGenerator(0xd68755941d99d05e, nodes, genoptions{})
		v := nodes[test.constID]
		if v == nil {
			t.Errorf("Can't find const @%#x for %s test", test.constID, test.name)
			return nil, schema.Type{}, nil, false
		}
		if v.Which() != schema.Node_Which_const {
			t.Errorf("Type of node @%#x in %s test is a %v node; want const. Check the test.", test.constID, test.name, v.Which())
			return nil, schema.Type{}, nil, false
		}
		constType, _ := v.Const().Type()
		for _, i := range test.initImports {
			g.imports.add(i)
		}
		return g, constType, v, true
	}
	for _, test := range tests {
		g, typ, from, ok := collect(test)
		if !ok {
			continue
		}
		rn, err := g.RemoteTypeName(typ, from)
		if err != nil {
			t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]) error: %v", test.name, test.constID, test.constID, err)
			continue
		}
		if rn != test.remoteName {
			t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]) = %q; want %q", test.name, test.constID, test.constID, rn, test.remoteName)
			continue
		}
		if !hasExactImports(test.imports, g.imports) {
			t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]); g.imports = %s; want %s", test.name, test.constID, test.constID, formatImportSpecs(g.imports.usedImports()), formatImportSpecs(test.imports))
			continue
		}
	}
	for _, test := range tests {
		g, typ, from, ok := collect(test)
		if !ok {
			continue
		}
		rn, err := g.RemoteTypeNew(typ, from)
		if err != nil {
			t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]) error: %v", test.name, test.constID, test.constID, err)
			continue
		}
		if rn != test.remoteNew {
			t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]) = %q; want %q", test.name, test.constID, test.constID, rn, test.remoteNew)
			continue
		}
		if !hasExactImports(test.imports, g.imports) {
			t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]); g.imports = %s; want %s", test.name, test.constID, test.constID, formatImportSpecs(g.imports.usedImports()), formatImportSpecs(test.imports))
			continue
		}
	}
}

func hasExactImports(specs []importSpec, imp imports) bool {
	used := imp.usedImports()
	if len(used) != len(specs) {
		return false
	}
outer:
	for i := range specs {
		for j := range used {
			if specs[i] == used[j] {
				continue outer
			}
		}
		return false
	}
	return true
}

func formatImportSpecs(specs []importSpec) string {
	var buf bytes.Buffer
	for i, s := range specs {
		if i > 0 {
			buf.WriteString("; ")
		}
		buf.WriteString(s.String())
	}
	return buf.String()
}

func TestDefineConstNodes(t *testing.T) {
	req := mustReadGeneratorRequest(t, "const.capnp.out")
	nodes, err := buildNodeMap(req)
	if err != nil {
		t.Fatal("buildNodeMap:", err)
	}
	g := newGenerator(0xc260cb50ae622e10, nodes, genoptions{})
	getCalls := traceGenerator(g)
	err = g.defineConstNodes(nodes[0xc260cb50ae622e10].nodes)
	if err != nil {
		t.Fatal("defineConstNodes:", err)
	}
	calls := getCalls()
	if len(calls) != 1 {
		t.Fatalf("defineConstNodes called %d templates; want 1", len(calls))
	}
	p, ok := calls[0].params.(constantsParams)
	if calls[0].name != "constants" || !ok {
		t.Fatalf("defineConstNodes rendered %v; want render of constants template", calls[0])
	}
	if !containsExactlyIDs(p.Consts, 0xda96e2255811b258) {
		// TODO(#20): print nodes better
		t.Errorf("defineConstNodes rendered Consts %v", p.Consts)
	}
	if !containsExactlyIDs(p.Vars, 0xe0a385c7be1fea4d) {
		// TODO(#20): print nodes better
		t.Errorf("defineConstNodes rendered Vars %v", p.Vars)
	}
}

func TestDefineFile(t *testing.T) {
	// Sanity check to make sure schemas don't error on generation.

	tests := []struct {
		fileID uint64
		fname  string
		opts   genoptions
	}{
		{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{promises: true}},
		{0xb312981b2552a250, "rpc.capnp.out", genoptions{promises: true}},
		{0xd68755941d99d05e, "scopes.capnp.out", genoptions{promises: true}},
		{0xecd50d792c3d9992, "util.capnp.out", genoptions{promises: true}},
	}
	for _, test := range tests {
		data, err := readTestFile(test.fname)
		if err != nil {
			t.Errorf("reading %s: %v", test.fname, err)
			continue
		}
		msg, err := capnp.Unmarshal(data)
		if err != nil {
			t.Errorf("Unmarshaling %s: %v", test.fname, err)
			continue
		}
		req, err := schema.ReadRootCodeGeneratorRequest(msg)
		if err != nil {
			t.Errorf("Reading code generator request %s: %v", test.fname, err)
			continue
		}
		nodes, err := buildNodeMap(req)
		if err != nil {
			t.Errorf("buildNodeMap %s: %v", test.fname, err)
			continue
		}
		g := newGenerator(test.fileID, nodes, test.opts)
		if err := g.defineFile(); err != nil {
			t.Errorf("defineFile %s: %v", test.fname, err)
			continue
		}
	}
}

type traceRenderer struct {
	renderer
	calls []renderCall
}

func traceGenerator(g *generator) (getCalls func() []renderCall) {
	tr := &traceRenderer{renderer: g.r}
	g.r = tr
	return func() []renderCall { return tr.calls }
}

func (tr *traceRenderer) Render(name string, params interface{}) error {
	tr.calls = append(tr.calls, renderCall{name, params})
	return tr.renderer.Render(name, params)
}

type renderCall struct {
	name   string
	params interface{}
}

func (rc renderCall) String() string {
	return fmt.Sprintf("{%q %#v}", rc.name, rc.params)
}

func containsExactlyIDs(nodes []*node, ids ...uint64) bool {
	if len(nodes) != len(ids) {
		return false
	}
	sorted := make([]uint64, len(ids))
	copy(sorted, ids)
	sort.Sort(uint64Slice(sorted))
	actual := make([]uint64, len(nodes))
	for i := range nodes {
		actual[i] = nodes[i].Id()
	}
	sort.Sort(uint64Slice(actual))
	for i := range sorted {
		if actual[i] != sorted[i] {
			return false
		}
	}
	return true
}

type uint64Slice []uint64

func (a uint64Slice) Len() int {
	return len(a)
}

func (a uint64Slice) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a uint64Slice) Less(i, j int) bool {
	return a[i] < a[j]
}
<commit_msg>capnpc-go: add parse check to DefineFile<commit_after>package main

import (
	"bytes"
	"fmt"
	"go/parser"
	"go/token"
	"io/ioutil"
	"path/filepath"
	"sort"
	"testing"

	"zombiezen.com/go/capnproto2"
	"zombiezen.com/go/capnproto2/internal/schema"
)

func readTestFile(name string) ([]byte, error) {
	path := filepath.Join("testdata", name)
	return ioutil.ReadFile(path)
}

func mustReadTestFile(t *testing.T, name string) []byte {
	data, err := readTestFile(name)
	if err != nil {
		t.Fatal(err)
	}
	return data
}

func mustReadGeneratorRequest(t *testing.T, name string) schema.CodeGeneratorRequest {
	data := mustReadTestFile(t, name)
	msg, err := capnp.Unmarshal(data)
	if err != nil {
		t.Fatalf("Unmarshaling %s: %v", name, err)
	}
	req, err := schema.ReadRootCodeGeneratorRequest(msg)
	if err != nil {
		t.Fatalf("Reading code generator request %s: %v", name, err)
	}
	return req
}

func TestGoCapnpNodeMap(t *testing.T) {
	req := mustReadGeneratorRequest(t, "go.capnp.out")
	nodes, err := buildNodeMap(req)
	if err != nil {
		t.Error("buildNodeMap:", err)
	}
	want := []uint64{
		0xd12a1c51fedd6c88,
		0xbea97f1023792be0,
		0xe130b601260e44b5,
		0xc58ad6bd519f935e,
		0xa574b41924caefc7,
		0xc8768679ec52e012,
		0xfa10659ae02f2093,
		0xc2b96012172f8df1,
	}
	for _, k := range want {
		if nodes[k] == nil {
			t.Errorf("missing @%#x from node map", k)
		}
	}
}

func TestRemoteScope(t *testing.T) {
	type scopeTest struct {
		name        string
		constID     uint64
		initImports []importSpec

		remoteName string
		remoteNew  string
		imports    []importSpec
	}
	tests := []scopeTest{
		{
			name:       "same-file struct",
			constID:    0x84efedc75e99768d, // scopes.fooVar
			remoteName: "Foo",
			remoteNew:  "NewFoo",
		},
		{
			name:       "different file struct",
			constID:    0x836faf1834d91729, // scopes.otherFooVar
			remoteName: "otherscopes.Foo",
			remoteNew:  "otherscopes.NewFoo",
			imports: []importSpec{
				{name: "otherscopes", path: "zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes"},
			},
		},
		{
			name:       "same-file struct list",
			constID:    0xcda2680ec5c921e0, // scopes.fooListVar
			remoteName: "Foo_List",
			remoteNew:  "NewFoo_List",
		},
		{
			name:       "different file struct list",
			constID:    0x83e7e1b3cd1be338, // scopes.otherFooListVar
			remoteName: "otherscopes.Foo_List",
			remoteNew:  "otherscopes.NewFoo_List",
			imports: []importSpec{
				{name: "otherscopes", path: "zombiezen.com/go/capnproto2/capnpc-go/testdata/otherscopes"},
			},
		},
		{
			name:       "built-in Int32 list",
			constID:    0xacf3d9917d0bb0f0, // scopes.intList
			remoteName: "capnp.Int32List",
			remoteNew:  "capnp.NewInt32List",
			imports: []importSpec{
				{name: "capnp", path: "zombiezen.com/go/capnproto2"},
			},
		},
	}
	req := mustReadGeneratorRequest(t, "scopes.capnp.out")
	nodes, err := buildNodeMap(req)
	if err != nil {
		t.Fatal("buildNodeMap:", err)
	}
	collect := func(test scopeTest) (g *generator, typ schema.Type, from *node, ok bool) {
		g = newGenerator(0xd68755941d99d05e, nodes, genoptions{})
		v := nodes[test.constID]
		if v == nil {
			t.Errorf("Can't find const @%#x for %s test", test.constID, test.name)
			return nil, schema.Type{}, nil, false
		}
		if v.Which() != schema.Node_Which_const {
			t.Errorf("Type of node @%#x in %s test is a %v node; want const. Check the test.", test.constID, test.name, v.Which())
			return nil, schema.Type{}, nil, false
		}
		constType, _ := v.Const().Type()
		for _, i := range test.initImports {
			g.imports.add(i)
		}
		return g, constType, v, true
	}
	for _, test := range tests {
		g, typ, from, ok := collect(test)
		if !ok {
			continue
		}
		rn, err := g.RemoteTypeName(typ, from)
		if err != nil {
			t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]) error: %v", test.name, test.constID, test.constID, err)
			continue
		}
		if rn != test.remoteName {
			t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]) = %q; want %q", test.name, test.constID, test.constID, rn, test.remoteName)
			continue
		}
		if !hasExactImports(test.imports, g.imports) {
			t.Errorf("%s: g.RemoteTypeName(nodes[%#x].Const().Type(), nodes[%#x]); g.imports = %s; want %s", test.name, test.constID, test.constID, formatImportSpecs(g.imports.usedImports()), formatImportSpecs(test.imports))
			continue
		}
	}
	for _, test := range tests {
		g, typ, from, ok := collect(test)
		if !ok {
			continue
		}
		rn, err := g.RemoteTypeNew(typ, from)
		if err != nil {
			t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]) error: %v", test.name, test.constID, test.constID, err)
			continue
		}
		if rn != test.remoteNew {
			t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]) = %q; want %q", test.name, test.constID, test.constID, rn, test.remoteNew)
			continue
		}
		if !hasExactImports(test.imports, g.imports) {
			t.Errorf("%s: g.RemoteTypeNew(nodes[%#x].Const().Type(), nodes[%#x]); g.imports = %s; want %s", test.name, test.constID, test.constID, formatImportSpecs(g.imports.usedImports()), formatImportSpecs(test.imports))
			continue
		}
	}
}

func hasExactImports(specs []importSpec, imp imports) bool {
	used := imp.usedImports()
	if len(used) != len(specs) {
		return false
	}
outer:
	for i := range specs {
		for j := range used {
			if specs[i] == used[j] {
				continue outer
			}
		}
		return false
	}
	return true
}

func formatImportSpecs(specs []importSpec) string {
	var buf bytes.Buffer
	for i, s := range specs {
		if i > 0 {
			buf.WriteString("; ")
		}
		buf.WriteString(s.String())
	}
	return buf.String()
}

func TestDefineConstNodes(t *testing.T) {
	req := mustReadGeneratorRequest(t, "const.capnp.out")
	nodes, err := buildNodeMap(req)
	if err != nil {
		t.Fatal("buildNodeMap:", err)
	}
	g := newGenerator(0xc260cb50ae622e10, nodes, genoptions{})
	getCalls := traceGenerator(g)
	err = g.defineConstNodes(nodes[0xc260cb50ae622e10].nodes)
	if err != nil {
		t.Fatal("defineConstNodes:", err)
	}
	calls := getCalls()
	if len(calls) != 1 {
		t.Fatalf("defineConstNodes called %d templates; want 1", len(calls))
	}
	p, ok := calls[0].params.(constantsParams)
	if calls[0].name != "constants" || !ok {
		t.Fatalf("defineConstNodes rendered %v; want render of constants template", calls[0])
	}
	if !containsExactlyIDs(p.Consts, 0xda96e2255811b258) {
		// TODO(#20): print nodes better
		t.Errorf("defineConstNodes rendered Consts %v", p.Consts)
	}
	if !containsExactlyIDs(p.Vars, 0xe0a385c7be1fea4d) {
		// TODO(#20): print nodes better
		t.Errorf("defineConstNodes rendered Vars %v", p.Vars)
	}
}

func TestDefineFile(t *testing.T) {
	// Sanity check to make sure codegen produces parseable Go.

	tests := []struct {
		fileID uint64
		fname  string
		opts   genoptions
	}{
		{0x832bcc6686a26d56, "aircraft.capnp.out", genoptions{promises: true}},
		{0xb312981b2552a250, "rpc.capnp.out", genoptions{promises: true}},
		{0xd68755941d99d05e, "scopes.capnp.out", genoptions{promises: true}},
		{0xecd50d792c3d9992, "util.capnp.out", genoptions{promises: true}},
	}
	for _, test := range tests {
		data, err := readTestFile(test.fname)
		if err != nil {
			t.Errorf("reading %s: %v", test.fname, err)
			continue
		}
		msg, err := capnp.Unmarshal(data)
		if err != nil {
			t.Errorf("Unmarshaling %s: %v", test.fname, err)
			continue
		}
		req, err := schema.ReadRootCodeGeneratorRequest(msg)
		if err != nil {
			t.Errorf("Reading code generator request %s: %v", test.fname, err)
			continue
		}
		nodes, err := buildNodeMap(req)
		if err != nil {
			t.Errorf("buildNodeMap %s: %v", test.fname, err)
			continue
		}
		g := newGenerator(test.fileID, nodes, test.opts)
		if err := g.defineFile(); err != nil {
			t.Errorf("defineFile %s: %v", test.fname, err)
			continue
		}
		src := g.generate()
		if _, err := parser.ParseFile(token.NewFileSet(), test.fname+".go", src, 0); err != nil {
			// TODO(light): log src
			t.Errorf("generate %s failed to parse: %v", test.fname, err)
			continue
		}
	}
}

type traceRenderer struct {
	renderer
	calls []renderCall
}

func traceGenerator(g *generator) (getCalls func() []renderCall) {
	tr := &traceRenderer{renderer: g.r}
	g.r = tr
	return func() []renderCall { return tr.calls }
}

func (tr *traceRenderer) Render(name string, params interface{}) error {
	tr.calls = append(tr.calls, renderCall{name, params})
	return tr.renderer.Render(name, params)
}

type renderCall struct {
	name   string
	params interface{}
}

func (rc renderCall) String() string {
	return fmt.Sprintf("{%q %#v}", rc.name, rc.params)
}

func containsExactlyIDs(nodes []*node, ids ...uint64) bool {
	if len(nodes) != len(ids) {
		return false
	}
	sorted := make([]uint64, len(ids))
	copy(sorted, ids)
	sort.Sort(uint64Slice(sorted))
	actual := make([]uint64, len(nodes))
	for i := range nodes {
		actual[i] = nodes[i].Id()
	}
	sort.Sort(uint64Slice(actual))
	for i := range sorted {
		if actual[i] != sorted[i] {
			return false
		}
	}
	return true
}

type uint64Slice []uint64

func (a uint64Slice) Len() int {
	return len(a)
}

func (a uint64Slice) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}

func (a uint64Slice) Less(i, j int) bool {
	return a[i] < a[j]
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"io"
	"log"
	"net/http"
	"os"
	"time"
)

func maybeFatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func setupDb(u string) {
	req, err := http.NewRequest("PUT", u, nil)
	maybeFatal(err)
	res, err := http.DefaultClient.Do(req)
	maybeFatal(err)
	res.Body.Close()
}

func sendOne(u, k string, body []byte) {
	resp, err := http.DefaultClient.Post(u+"?ts="+k,
		"application/json", bytes.NewReader(body))
	maybeFatal(err)
	defer resp.Body.Close()
	if resp.StatusCode >= 300 || resp.StatusCode < 200 {
		log.Fatalf("HTTP Error on %v: %v", k, err)
	}
}

func main() {
	if len(os.Args) < 2 {
		log.Fatalf("Usage:  gzip -dc backup.gz | %v http://seriesly:3133/dbname",
			os.Args[0])
	}
	u := os.Args[1]
	setupDb(u)

	t := time.Tick(5 * time.Second)
	i := 0

	d := json.NewDecoder(os.Stdin)
	for {
		kv := map[string]*json.RawMessage{}

		err := d.Decode(&kv)
		if err == io.EOF {
			log.Printf("Done!")
			break
		}
		maybeFatal(err)

		var latestKey string
		for k, v := range kv {
			body := []byte(*v)
			sendOne(u, k, body)
			latestKey = k
		}

		i++
		select {
		case <-t:
			log.Printf("Processed %v items, latest was %v", i, latestKey)
		default:
		}
	}
}
<commit_msg>Allow skipping of already restored data from load<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"io"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/dustin/seriesly/timelib"
)

var min = flag.String("min", "", "minimum timestamp (RFC3339)")

func maybeFatal(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

func setupDb(u string) {
	req, err := http.NewRequest("PUT", u, nil)
	maybeFatal(err)
	res, err := http.DefaultClient.Do(req)
	maybeFatal(err)
	res.Body.Close()
}

func sendOne(u, k string, body []byte) {
	resp, err := http.DefaultClient.Post(u+"?ts="+k,
		"application/json", bytes.NewReader(body))
	maybeFatal(err)
	defer resp.Body.Close()
	if resp.StatusCode >= 300 || resp.StatusCode < 200 {
		log.Fatalf("HTTP Error on %v: %v", k, err)
	}
}

func parseMinTime() time.Time {
	tm, err := timelib.ParseTime(*min)
	if err != nil {
		tm = time.Time{}
	}
	return tm
}

func main() {
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatalf("Usage:  gzip -dc backup.gz | %v http://seriesly:3133/dbname",
			os.Args[0])
	}
	u := flag.Arg(0)
	setupDb(u)

	minTime := parseMinTime()

	t := time.Tick(5 * time.Second)
	i := 0

	d := json.NewDecoder(os.Stdin)
	for {
		kv := map[string]*json.RawMessage{}

		err := d.Decode(&kv)
		if err == io.EOF {
			log.Printf("Done!")
			break
		}
		maybeFatal(err)

		var latestKey string
		for k, v := range kv {
			if !minTime.IsZero() {
				thist, err := timelib.ParseTime(k)
				if err == nil && minTime.After(thist) {
					continue
				}
			}
			body := []byte(*v)
			sendOne(u, k, body)
			latestKey = k
		}

		i++
		select {
		case <-t:
			log.Printf("Processed %v items, latest was %v", i, latestKey)
		default:
		}
	}
}
<|endoftext|>


<commit_before>package decoders

import (
	"errors"
	"math"
	"regexp"
	"strconv"
	"log"
	"fmt"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

type SeadPacket struct {
	Type      byte
	Location  byte
	Timestamp float64
	Period    float64
	Count     int
	Data      float64
	Serial    int
}

var headerRegex *regexp.Regexp
var InvalidHeader = errors.New("Invalid header.")
var InvalidPacket = errors.New("Invalid packet.")

func init() {
	headerRegex, err := regexp.Compile(constants.HEADER_REGEX)
	if err != nil {
		log.Println("Regex compile error:", err)
	}
}

func DecodeHeader(packet []byte) (serial int, err error) {
	serialString := headerRegex.Find(packet)
	if serialString == nil {
		err = InvalidHeader
		return
	}
	serial, err = strconv.Atoi(string(serialString))
	return
}

// Extract data sent from sensor from request
func DecodePacket(buffer []byte) (packet SeadPacket, err error) {
	for i := 0; i < len(buffer); {
		datatype := buffer[i]
		i++

		// Switch on the type of data sent in the packet
		switch {
		case datatype == 'T':
			// Type
			packet.Type = buffer[i]
			i++
		case datatype == 'l':
			// Location
			packet.Location = buffer[i]
			i++
		case datatype == 't':
			// Timestamp
			packet.Timestamp, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'P':
			// Period separator
			packet.Period, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'C':
			// Count
			packet.Count, err = strconv.Atoi(string(buffer[i : i+2]))
			i += 2
		case datatype == 'D':
			// Data
			// if count isn't set, return error
			// TODO finish parsing data
		case datatype == 'S':
			// Serial
			packet.Serial, err = strconv.Atoi(string(buffer[i : i+6]))
			i += 6
		case datatype == 'X':
			return
		default:
			err = InvalidPacket
		}

		if err != nil {
			return
		}
	}
	err = InvalidPacket
	return
}

func doubleToAsciiTime(double_time float64) string {
	int_time := int(double_time)
	var days = math.Floor(double_time / (60 * 60 * 24))
	var hours = math.Floor((int_time % (60 * 60 * 24)) / (60 * 60))
	var minutes = math.Floor((int_time % (60 * 60)) / (60))
	var seconds = math.Floor((int_time % (60)) / (1))
	var milliseconds = math.Floor(((int_time * 1000) % 1000))
	var clock_time = math.Floor(((int_time * 12000) % 12))

	return fmt.Sprintf("%03d%02d%02d%02d%03d%02d", days, hours, minutes, seconds, milliseconds, clock_time)
}

func asciiTimeToDouble(asciiTime []byte) (float64, error) {
	return 0.0, nil
}
<commit_msg>Fixed compile error.<commit_after>package decoders

import (
	"errors"
	"math"
	"regexp"
	"strconv"
	"log"
	"fmt"

	"github.com/seadsystem/Backend/DB/landingzone/constants"
)

type SeadPacket struct {
	Type      byte
	Location  byte
	Timestamp float64
	Period    float64
	Count     int
	Data      float64
	Serial    int
}

var headerRegex *regexp.Regexp
var InvalidHeader = errors.New("Invalid header.")
var InvalidPacket = errors.New("Invalid packet.")

func init() {
	headerRegex, err := regexp.Compile(constants.HEADER_REGEX)
	if err != nil {
		log.Println("Regex compile error:", err)
	}
}

func DecodeHeader(packet []byte) (serial int, err error) {
	serialString := headerRegex.Find(packet)
	if serialString == nil {
		err = InvalidHeader
		return
	}
	serial, err = strconv.Atoi(string(serialString))
	return
}

// Extract data sent from sensor from request
func DecodePacket(buffer []byte) (packet SeadPacket, err error) {
	for i := 0; i < len(buffer); {
		datatype := buffer[i]
		i++

		// Switch on the type of data sent in the packet
		switch {
		case datatype == 'T':
			// Type
			packet.Type = buffer[i]
			i++
		case datatype == 'l':
			// Location
			packet.Location = buffer[i]
			i++
		case datatype == 't':
			// Timestamp
			packet.Timestamp, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'P':
			// Period separator
			packet.Period, err = asciiTimeToDouble(buffer[i : i+14])
			i += 14
		case datatype == 'C':
			// Count
			packet.Count, err = strconv.Atoi(string(buffer[i : i+2]))
			i += 2
		case datatype == 'D':
			// Data
			// if count isn't set, return error
			// TODO finish parsing data
		case datatype == 'S':
			// Serial
			packet.Serial, err = strconv.Atoi(string(buffer[i : i+6]))
			i += 6
		case datatype == 'X':
			return
		default:
			err = InvalidPacket
		}

		if err != nil {
			return
		}
	}
	err = InvalidPacket
	return
}

func doubleToAsciiTime(double_time float64) string {
	int_time := int(double_time)
	var days = math.Floor(double_time / (60 * 60 * 24))
	var hours = (int_time % (60 * 60 * 24)) / (60 * 60)
	var minutes = (int_time % (60 * 60)) / 60
	var seconds = (int_time % (60)) / 1
	var milliseconds = (int_time * 1000) % 1000
	var clock_time = (int_time * 12000) % 12

	return fmt.Sprintf("%03d%02d%02d%02d%03d%02d", days, hours, minutes, seconds, milliseconds, clock_time)
}

func asciiTimeToDouble(asciiTime []byte) (float64, error) {
	return 0.0, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/gocql/gocql"
)

// write aggregated data to cassandra.

/*
https://godoc.org/github.com/gocql/gocql#Session
Session is the interface used by users to interact with the database.
It's safe for concurrent use by multiple goroutines and a typical usage scenario is to have one global session
object to interact with the whole Cassandra cluster.
*/
var cSession *gocql.Session

func InitCassandra() error {
	cluster := gocql.NewCluster(cassandraAddrs...)
	cluster.Keyspace = "raintank"
	cluster.Consistency = gocql.One
	var err error
	cSession, err = cluster.CreateSession()
	return err
}

// Insert metric into Cassandra.
//
// key: is the metric_id
// ts: is the start of the aggregated time range.
// data: is the payload as bytes.
func InsertMetric(key string, ts uint32, data []byte) error {
	query := "INSERT INTO metric (key, ts, data) values(?,?,?)"
	row_key := fmt.Sprintf("%s_%d", key, (ts / 3600 / 24 / 28)) // "month number" based on unix timestamp (rounded down)
	return cSession.Query(query, row_key, ts, data).Exec()
}
<commit_msg>add support for querying cassandra<commit_after>package main

import (
	"fmt"

	"github.com/gocql/gocql"
)

// write aggregated data to cassandra.

/*
https://godoc.org/github.com/gocql/gocql#Session
Session is the interface used by users to interact with the database.
It's safe for concurrent use by multiple goroutines and a typical usage scenario is to have one global session
object to interact with the whole Cassandra cluster.
*/
var cSession *gocql.Session

func InitCassandra() error {
	cluster := gocql.NewCluster(cassandraAddrs...)
	cluster.Keyspace = "raintank"
	cluster.Consistency = gocql.One
	var err error
	cSession, err = cluster.CreateSession()
	return err
}

// Insert metric into Cassandra.
//
// key: is the metric_id
// ts: is the start of the aggregated time range.
// data: is the payload as bytes.
func InsertMetric(key string, ts uint32, data []byte) error {
	query := "INSERT INTO metric (key, ts, data) values(?,?,?)"
	row_key := fmt.Sprintf("%s_%d", key, (ts / 3600 / 24 / 28)) // "month number" based on unix timestamp (rounded down)
	return cSession.Query(query, row_key, ts, data).Exec()
}

// Basic search of cassandra.
// TODO:(Awoods) this needs to be refactored to run each query in a separate goroutine.
// then concatenate the results at the end.
func SearchCassandra(key string, start, end time.Time) ([]Point, error) {
	start_epoch := uint32(start.Unix())
	end_epoch := uint32(end.Unix())
	points := make([]Point, 0)
	//get row_keys we need to query.
	row_ts, err := MonthsInRange(start, end)
	if err != nil {
		return points, err
	}
	// we need to send a query for each row holding data.
	// this is 1 row per calender month.
	num_rows := len(row_ts)

	for i, ts := range row_ts {
		row_key := fmt.Sprintf("%s_%s", key, ts)
		var query string
		params := make([]interface{}, 0)

		// all queries need the row_key
		params = append(params, row_key)
		if num_rows == 1 {
			// we need a selection of the row between startTs and endTs
			query = "SELECT data FROM metric WHERE key = ? AND ts < ? AND ts >= ?"
			params = append(params, end, start)
		} else if i < 1 {
			// we want from startTs to the end of the row.
			query = "SELECT data FROM metric WHERE key = ? AND ts >= ?"
			params = append(params, start)
		} else {
			// we want from start of the row till the endTs.
			query = "SELECT data FROM metric WHERE key = ? AND ts < ?"
			params = append(params, end)
		}
		var b []byte
		result := cSession.Query(query, params...).Iter()
		for result.Scan(&b) {
			iter, err := tsz.NewIterator(b)
			if err != nil {
				log.Fatal(err)
			}
			for iter.Next() {
				ts, val := iter.Values()
				if ts > start_epoch && ts <= end_epoch {
					points = append(points, Point{val, ts})
				}
			}
		}
	}
	return points, nil
}

func MonthsInRange(start, end time.Time) ([]string, error) {
	if start.After(end) {
		return nil, fmt.Errorf("start time after end time.")
	}
	months := make([]string, 0)

	year := start.Year()
	month := int(start.Month())

	if year != end.Year() {
		for month <= 12 {
			months = append(months, fmt.Sprintf("%d%02d", year, month))
			month++
		}
		//start new year.
		month = 1
		year++
	}

	for month <= int(end.Month()) {
		months = append(months, fmt.Sprintf("%d%02d", year, month))
		month++
	}
	return months, nil
}
<|endoftext|>


<commit_before>package wallet

import (
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/types"
)

// sortedOutputs is a struct containing a slice of siacoin outputs and their
// corresponding ids. sortedOutputs can be sorted using the sort package.
type sortedOutputs struct {
	ids     []types.SiacoinOutputID
	outputs []types.SiacoinOutput
}

// ConfirmedBalance returns the balance of the wallet according to all of the
// confirmed transactions.
func (w *Wallet) ConfirmedBalance() (siacoinBalance types.Currency, siafundBalance types.Currency, siafundClaimBalance types.Currency) {
	// ensure durability of reported balance
	w.mu.Lock()
	defer w.mu.Unlock()
	w.syncDB()

	dbForEachSiacoinOutput(w.dbTx, func(_ types.SiacoinOutputID, sco types.SiacoinOutput) {
		if sco.Value.Cmp(dustValue()) > 0 {
			siacoinBalance = siacoinBalance.Add(sco.Value)
		}
	})
	dbForEachSiafundOutput(w.dbTx, func(_ types.SiafundOutputID, sfo types.SiafundOutput) {
		siafundBalance = siafundBalance.Add(sfo.Value)
		siafundClaimBalance = siafundClaimBalance.Add(w.siafundPool.Sub(sfo.ClaimStart).Mul(sfo.Value).Div(types.SiafundCount))
	})
	return
}

// UnconfirmedBalance returns the number of outgoing and incoming siacoins in
// the unconfirmed transaction set. Refund outputs are included in this
// reporting.
func (w *Wallet) UnconfirmedBalance() (outgoingSiacoins types.Currency, incomingSiacoins types.Currency) {
	w.mu.Lock()
	defer w.mu.Unlock()

	for _, upt := range w.unconfirmedProcessedTransactions {
		for _, input := range upt.Inputs {
			if input.FundType == types.SpecifierSiacoinInput && input.WalletAddress {
				outgoingSiacoins = outgoingSiacoins.Add(input.Value)
			}
		}
		for _, output := range upt.Outputs {
			if output.FundType == types.SpecifierSiacoinOutput && output.WalletAddress && output.Value.Cmp(dustValue()) > 0 {
				incomingSiacoins = incomingSiacoins.Add(output.Value)
			}
		}
	}
	return
}

// SendSiacoins creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiacoins(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	output := types.SiacoinOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(amount.Add(tpoolFee))
	if err != nil {
		return nil, build.ExtendErr("unable to fund transaction", err)
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiacoinOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		return nil, build.ExtendErr("unable to sign transaction", err)
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		return nil, build.ExtendErr("unable to get transaction accepted", err)
	}
	return txnSet, nil
}

// SendSiafunds creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiafunds(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	output := types.SiafundOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(tpoolFee)
	if err != nil {
		return nil, err
	}
	err = txnBuilder.FundSiafunds(amount)
	if err != nil {
		return nil, err
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiafundOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		return nil, err
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		return nil, err
	}
	return txnSet, nil
}

// Len returns the number of elements in the sortedOutputs struct.
func (so sortedOutputs) Len() int {
	if build.DEBUG && len(so.ids) != len(so.outputs) {
		panic("sortedOutputs object is corrupt")
	}
	return len(so.ids)
}

// Less returns whether element 'i' is less than element 'j'. The currency
// value of each output is used for comparison.
func (so sortedOutputs) Less(i, j int) bool {
	return so.outputs[i].Value.Cmp(so.outputs[j].Value) < 0
}

// Swap swaps two elements in the sortedOutputs set.
func (so sortedOutputs) Swap(i, j int) {
	so.ids[i], so.ids[j] = so.ids[j], so.ids[i]
	so.outputs[i], so.outputs[j] = so.outputs[j], so.outputs[i]
}
<commit_msg>increase fee when sending siafunds<commit_after>package wallet

import (
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/types"
)

// sortedOutputs is a struct containing a slice of siacoin outputs and their
// corresponding ids. sortedOutputs can be sorted using the sort package.
type sortedOutputs struct {
	ids     []types.SiacoinOutputID
	outputs []types.SiacoinOutput
}

// ConfirmedBalance returns the balance of the wallet according to all of the
// confirmed transactions.
func (w *Wallet) ConfirmedBalance() (siacoinBalance types.Currency, siafundBalance types.Currency, siafundClaimBalance types.Currency) {
	// ensure durability of reported balance
	w.mu.Lock()
	defer w.mu.Unlock()
	w.syncDB()

	dbForEachSiacoinOutput(w.dbTx, func(_ types.SiacoinOutputID, sco types.SiacoinOutput) {
		if sco.Value.Cmp(dustValue()) > 0 {
			siacoinBalance = siacoinBalance.Add(sco.Value)
		}
	})
	dbForEachSiafundOutput(w.dbTx, func(_ types.SiafundOutputID, sfo types.SiafundOutput) {
		siafundBalance = siafundBalance.Add(sfo.Value)
		siafundClaimBalance = siafundClaimBalance.Add(w.siafundPool.Sub(sfo.ClaimStart).Mul(sfo.Value).Div(types.SiafundCount))
	})
	return
}

// UnconfirmedBalance returns the number of outgoing and incoming siacoins in
// the unconfirmed transaction set. Refund outputs are included in this
// reporting.
func (w *Wallet) UnconfirmedBalance() (outgoingSiacoins types.Currency, incomingSiacoins types.Currency) {
	w.mu.Lock()
	defer w.mu.Unlock()

	for _, upt := range w.unconfirmedProcessedTransactions {
		for _, input := range upt.Inputs {
			if input.FundType == types.SpecifierSiacoinInput && input.WalletAddress {
				outgoingSiacoins = outgoingSiacoins.Add(input.Value)
			}
		}
		for _, output := range upt.Outputs {
			if output.FundType == types.SpecifierSiacoinOutput && output.WalletAddress && output.Value.Cmp(dustValue()) > 0 {
				incomingSiacoins = incomingSiacoins.Add(output.Value)
			}
		}
	}
	return
}

// SendSiacoins creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiacoins(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	output := types.SiacoinOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(amount.Add(tpoolFee))
	if err != nil {
		return nil, build.ExtendErr("unable to fund transaction", err)
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiacoinOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		return nil, build.ExtendErr("unable to sign transaction", err)
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		return nil, build.ExtendErr("unable to get transaction accepted", err)
	}
	return txnSet, nil
}

// SendSiafunds creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiafunds(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	tpoolFee = tpoolFee.Mul64(5)   // use large fee to ensure siafund transactions are selected by miners
	output := types.SiafundOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(tpoolFee)
	if err != nil {
		return nil, err
	}
	err = txnBuilder.FundSiafunds(amount)
	if err != nil {
		return nil, err
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiafundOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		return nil, err
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		return nil, err
	}
	return txnSet, nil
}

// Len returns the number of elements in the sortedOutputs struct.
func (so sortedOutputs) Len() int {
	if build.DEBUG && len(so.ids) != len(so.outputs) {
		panic("sortedOutputs object is corrupt")
	}
	return len(so.ids)
}

// Less returns whether element 'i' is less than element 'j'. The currency
// value of each output is used for comparison.
func (so sortedOutputs) Less(i, j int) bool {
	return so.outputs[i].Value.Cmp(so.outputs[j].Value) < 0
}

// Swap swaps two elements in the sortedOutputs set.
func (so sortedOutputs) Swap(i, j int) {
	so.ids[i], so.ids[j] = so.ids[j], so.ids[i]
	so.outputs[i], so.outputs[j] = so.outputs[j], so.outputs[i]
}
<|endoftext|>


<commit_before>package wallet

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// sortedOutputs is a struct containing a slice of siacoin outputs and their
// corresponding ids. sortedOutputs can be sorted using the sort package.
type sortedOutputs struct {
	ids     []types.SiacoinOutputID
	outputs []types.SiacoinOutput
}

// DustThreshold returns the quantity per byte below which a Currency is
// considered to be Dust.
func (w *Wallet) DustThreshold() types.Currency {
	minFee, _ := w.tpool.FeeEstimation()
	return minFee.Mul64(3)
}

// ConfirmedBalance returns the balance of the wallet according to all of the
// confirmed transactions.
func (w *Wallet) ConfirmedBalance() (siacoinBalance types.Currency, siafundBalance types.Currency, siafundClaimBalance types.Currency) {
	// dustThreshold has to be obtained separate from the lock
	dustThreshold := w.DustThreshold()

	w.mu.Lock()
	defer w.mu.Unlock()

	// ensure durability of reported balance
	w.syncDB()

	dbForEachSiacoinOutput(w.dbTx, func(_ types.SiacoinOutputID, sco types.SiacoinOutput) {
		if sco.Value.Cmp(dustThreshold) > 0 {
			siacoinBalance = siacoinBalance.Add(sco.Value)
		}
	})

	siafundPool, err := dbGetSiafundPool(w.dbTx)
	if err != nil {
		return
	}
	dbForEachSiafundOutput(w.dbTx, func(_ types.SiafundOutputID, sfo types.SiafundOutput) {
		siafundBalance = siafundBalance.Add(sfo.Value)
		if sfo.ClaimStart.Cmp(siafundPool) > 0 {
			// Skip claims larger than the siafund pool. This should only
			// occur if the siafund pool has not been initialized yet.
			w.log.Debugf("skipping claim with start value %v because siafund pool is only %v", sfo.ClaimStart, siafundPool)
			return
		}
		siafundClaimBalance = siafundClaimBalance.Add(siafundPool.Sub(sfo.ClaimStart).Mul(sfo.Value).Div(types.SiafundCount))
	})
	return
}

// UnconfirmedBalance returns the number of outgoing and incoming siacoins in
// the unconfirmed transaction set. Refund outputs are included in this
// reporting.
func (w *Wallet) UnconfirmedBalance() (outgoingSiacoins types.Currency, incomingSiacoins types.Currency) {
	// dustThreshold has to be obtained separate from the lock
	dustThreshold := w.DustThreshold()

	w.mu.Lock()
	defer w.mu.Unlock()

	for _, upt := range w.unconfirmedProcessedTransactions {
		for _, input := range upt.Inputs {
			if input.FundType == types.SpecifierSiacoinInput && input.WalletAddress {
				outgoingSiacoins = outgoingSiacoins.Add(input.Value)
			}
		}
		for _, output := range upt.Outputs {
			if output.FundType == types.SpecifierSiacoinOutput && output.WalletAddress && output.Value.Cmp(dustThreshold) > 0 {
				incomingSiacoins = incomingSiacoins.Add(output.Value)
			}
		}
	}
	return
}

// SendSiacoins creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiacoins(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()
	if !w.unlocked {
		w.log.Println("Attempt to send coins has failed - wallet is locked")
		return nil, modules.ErrLockedWallet
	}

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	output := types.SiacoinOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(amount.Add(tpoolFee))
	if err != nil {
		w.log.Println("Attempt to send coins has failed - failed to fund transaction:", err)
		return nil, build.ExtendErr("unable to fund transaction", err)
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiacoinOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - failed to sign transaction:", err)
		return nil, build.ExtendErr("unable to sign transaction", err)
	}
	if w.deps.disrupt("SendSiacoinsInterrupted") {
		return nil, errors.New("Failed to accept transaction set. (SendSiacoinsInterrupted)")
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - transaction pool rejected transaction:", err)
		return nil, build.ExtendErr("unable to get transaction accepted", err)
	}
	w.log.Println("Submitted a siacoin transfer transaction set for value", amount.HumanString(), "with fees", tpoolFee.HumanString(), "IDs:")
	for _, txn := range txnSet {
		w.log.Println("\t", txn.ID())
	}
	return txnSet, nil
}

// SendSiacoinsMulti creates a transaction that includes the specified
// outputs. The transaction is submitted to the transaction pool and is also
// returned.
func (w *Wallet) SendSiacoinsMulti(outputs []types.SiacoinOutput) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()
	if !w.unlocked {
		w.log.Println("Attempt to send coins has failed - wallet is locked")
		return nil, modules.ErrLockedWallet
	}

	txnBuilder := w.StartTransaction()

	// Add estimated transaction fee.
	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(2)                              // We don't want send-to-many transactions to fail.
	tpoolFee = tpoolFee.Mul64(1000 + 60*uint64(len(outputs))) // Estimated transaction size in bytes
	txnBuilder.AddMinerFee(tpoolFee)

	// Calculate total cost to wallet.
	// NOTE: we only want to call FundSiacoins once; that way, it will
	// (ideally) fund the entire transaction with a single input, instead of
	// many smaller ones.
	totalCost := tpoolFee
	for _, sco := range outputs {
		totalCost = totalCost.Add(sco.Value)
	}
	err := txnBuilder.FundSiacoins(totalCost)
	if err != nil {
		return nil, build.ExtendErr("unable to fund transaction", err)
	}

	for _, sco := range outputs {
		txnBuilder.AddSiacoinOutput(sco)
	}

	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - failed to sign transaction:", err)
		return nil, build.ExtendErr("unable to sign transaction", err)
	}
	if w.deps.disrupt("SendSiacoinsInterrupted") {
		return nil, errors.New("Failed to accept transaction set. (SendSiacoinsInterrupted)")
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - transaction pool rejected transaction:", err)
		return nil, build.ExtendErr("unable to get transaction accepted", err)
	}
	return txnSet, nil
}

// SendSiafunds creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiafunds(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()
	if !w.unlocked {
		return nil, modules.ErrLockedWallet
	}

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	tpoolFee = tpoolFee.Mul64(5)   // use large fee to ensure siafund transactions are selected by miners
	output := types.SiafundOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(tpoolFee)
	if err != nil {
		return nil, err
	}
	err = txnBuilder.FundSiafunds(amount)
	if err != nil {
		return nil, err
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiafundOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		return nil, err
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		return nil, err
	}
	w.log.Println("Submitted a siafund transfer transaction set for value", amount.HumanString(), "with fees", tpoolFee.HumanString(), "IDs:")
	for _, txn := range txnSet {
		w.log.Println("\t", txn.ID())
	}
	return txnSet, nil
}

// Len returns the number of elements in the sortedOutputs struct.
func (so sortedOutputs) Len() int {
	if build.DEBUG && len(so.ids) != len(so.outputs) {
		panic("sortedOutputs object is corrupt")
	}
	return len(so.ids)
}

// Less returns whether element 'i' is less than element 'j'. The currency
// value of each output is used for comparison.
func (so sortedOutputs) Less(i, j int) bool {
	return so.outputs[i].Value.Cmp(so.outputs[j].Value) < 0
}

// Swap swaps two elements in the sortedOutputs set.
func (so sortedOutputs) Swap(i, j int) {
	so.ids[i], so.ids[j] = so.ids[j], so.ids[i]
	so.outputs[i], so.outputs[j] = so.outputs[j], so.outputs[i]
}
<commit_msg>fix failing testcase<commit_after>package wallet

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// sortedOutputs is a struct containing a slice of siacoin outputs and their
// corresponding ids. sortedOutputs can be sorted using the sort package.
type sortedOutputs struct {
	ids     []types.SiacoinOutputID
	outputs []types.SiacoinOutput
}

// DustThreshold returns the quantity per byte below which a Currency is
// considered to be Dust.
func (w *Wallet) DustThreshold() types.Currency {
	minFee, _ := w.tpool.FeeEstimation()
	return minFee.Mul64(3)
}

// ConfirmedBalance returns the balance of the wallet according to all of the
// confirmed transactions.
func (w *Wallet) ConfirmedBalance() (siacoinBalance types.Currency, siafundBalance types.Currency, siafundClaimBalance types.Currency) {
	// dustThreshold has to be obtained separate from the lock
	dustThreshold := w.DustThreshold()

	w.mu.Lock()
	defer w.mu.Unlock()

	// ensure durability of reported balance
	w.syncDB()

	dbForEachSiacoinOutput(w.dbTx, func(_ types.SiacoinOutputID, sco types.SiacoinOutput) {
		if sco.Value.Cmp(dustThreshold) > 0 {
			siacoinBalance = siacoinBalance.Add(sco.Value)
		}
	})

	siafundPool, err := dbGetSiafundPool(w.dbTx)
	if err != nil {
		return
	}
	dbForEachSiafundOutput(w.dbTx, func(_ types.SiafundOutputID, sfo types.SiafundOutput) {
		siafundBalance = siafundBalance.Add(sfo.Value)
		if sfo.ClaimStart.Cmp(siafundPool) > 0 {
			// Skip claims larger than the siafund pool. This should only
			// occur if the siafund pool has not been initialized yet.
			w.log.Debugf("skipping claim with start value %v because siafund pool is only %v", sfo.ClaimStart, siafundPool)
			return
		}
		siafundClaimBalance = siafundClaimBalance.Add(siafundPool.Sub(sfo.ClaimStart).Mul(sfo.Value).Div(types.SiafundCount))
	})
	return
}

// UnconfirmedBalance returns the number of outgoing and incoming siacoins in
// the unconfirmed transaction set. Refund outputs are included in this
// reporting.
func (w *Wallet) UnconfirmedBalance() (outgoingSiacoins types.Currency, incomingSiacoins types.Currency) {
	// dustThreshold has to be obtained separate from the lock
	dustThreshold := w.DustThreshold()

	w.mu.Lock()
	defer w.mu.Unlock()

	for _, upt := range w.unconfirmedProcessedTransactions {
		for _, input := range upt.Inputs {
			if input.FundType == types.SpecifierSiacoinInput && input.WalletAddress {
				outgoingSiacoins = outgoingSiacoins.Add(input.Value)
			}
		}
		for _, output := range upt.Outputs {
			if output.FundType == types.SpecifierSiacoinOutput && output.WalletAddress && output.Value.Cmp(dustThreshold) > 0 {
				incomingSiacoins = incomingSiacoins.Add(output.Value)
			}
		}
	}
	return
}

// SendSiacoins creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiacoins(amount types.Currency, dest types.UnlockHash) (txns []types.Transaction, err error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()
	if !w.unlocked {
		w.log.Println("Attempt to send coins has failed - wallet is locked")
		return nil, modules.ErrLockedWallet
	}

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	output := types.SiacoinOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	defer func() {
		if err != nil {
			txnBuilder.Drop()
		}
	}()
	err = txnBuilder.FundSiacoins(amount.Add(tpoolFee))
	if err != nil {
		w.log.Println("Attempt to send coins has failed - failed to fund transaction:", err)
		return nil, build.ExtendErr("unable to fund transaction", err)
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiacoinOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - failed to sign transaction:", err)
		return nil, build.ExtendErr("unable to sign transaction", err)
	}
	if w.deps.disrupt("SendSiacoinsInterrupted") {
		return nil, errors.New("Failed to accept transaction set. (SendSiacoinsInterrupted)")
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - transaction pool rejected transaction:", err)
		return nil, build.ExtendErr("unable to get transaction accepted", err)
	}
	w.log.Println("Submitted a siacoin transfer transaction set for value", amount.HumanString(), "with fees", tpoolFee.HumanString(), "IDs:")
	for _, txn := range txnSet {
		w.log.Println("\t", txn.ID())
	}
	return txnSet, nil
}

// SendSiacoinsMulti creates a transaction that includes the specified
// outputs. The transaction is submitted to the transaction pool and is also
// returned.
func (w *Wallet) SendSiacoinsMulti(outputs []types.SiacoinOutput) (txns []types.Transaction, err error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()
	if !w.unlocked {
		w.log.Println("Attempt to send coins has failed - wallet is locked")
		return nil, modules.ErrLockedWallet
	}

	txnBuilder := w.StartTransaction()
	defer func() {
		if err != nil {
			txnBuilder.Drop()
		}
	}()

	// Add estimated transaction fee.
	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(2)                              // We don't want send-to-many transactions to fail.
	tpoolFee = tpoolFee.Mul64(1000 + 60*uint64(len(outputs))) // Estimated transaction size in bytes
	txnBuilder.AddMinerFee(tpoolFee)

	// Calculate total cost to wallet.
	// NOTE: we only want to call FundSiacoins once; that way, it will
	// (ideally) fund the entire transaction with a single input, instead of
	// many smaller ones.
	totalCost := tpoolFee
	for _, sco := range outputs {
		totalCost = totalCost.Add(sco.Value)
	}
	err = txnBuilder.FundSiacoins(totalCost)
	if err != nil {
		return nil, build.ExtendErr("unable to fund transaction", err)
	}

	for _, sco := range outputs {
		txnBuilder.AddSiacoinOutput(sco)
	}

	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - failed to sign transaction:", err)
		return nil, build.ExtendErr("unable to sign transaction", err)
	}
	if w.deps.disrupt("SendSiacoinsInterrupted") {
		return nil, errors.New("Failed to accept transaction set. (SendSiacoinsInterrupted)")
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		w.log.Println("Attempt to send coins has failed - transaction pool rejected transaction:", err)
		return nil, build.ExtendErr("unable to get transaction accepted", err)
	}
	return txnSet, nil
}

// SendSiafunds creates a transaction sending 'amount' to 'dest'. The transaction
// is submitted to the transaction pool and is also returned.
func (w *Wallet) SendSiafunds(amount types.Currency, dest types.UnlockHash) ([]types.Transaction, error) {
	if err := w.tg.Add(); err != nil {
		return nil, err
	}
	defer w.tg.Done()
	if !w.unlocked {
		return nil, modules.ErrLockedWallet
	}

	_, tpoolFee := w.tpool.FeeEstimation()
	tpoolFee = tpoolFee.Mul64(750) // Estimated transaction size in bytes
	tpoolFee = tpoolFee.Mul64(5)   // use large fee to ensure siafund transactions are selected by miners
	output := types.SiafundOutput{
		Value:      amount,
		UnlockHash: dest,
	}

	txnBuilder := w.StartTransaction()
	err := txnBuilder.FundSiacoins(tpoolFee)
	if err != nil {
		return nil, err
	}
	err = txnBuilder.FundSiafunds(amount)
	if err != nil {
		return nil, err
	}
	txnBuilder.AddMinerFee(tpoolFee)
	txnBuilder.AddSiafundOutput(output)
	txnSet, err := txnBuilder.Sign(true)
	if err != nil {
		return nil, err
	}
	err = w.tpool.AcceptTransactionSet(txnSet)
	if err != nil {
		return nil, err
	}
	w.log.Println("Submitted a siafund transfer transaction set for value", amount.HumanString(), "with fees", tpoolFee.HumanString(), "IDs:")
	for _, txn := range txnSet {
		w.log.Println("\t", txn.ID())
	}
	return txnSet, nil
}

// Len returns the number of elements in the sortedOutputs struct.
func (so sortedOutputs) Len() int {
	if build.DEBUG && len(so.ids) != len(so.outputs) {
		panic("sortedOutputs object is corrupt")
	}
	return len(so.ids)
}

// Less returns whether element 'i' is less than element 'j'. The currency
// value of each output is used for comparison.
func (so sortedOutputs) Less(i, j int) bool {
	return so.outputs[i].Value.Cmp(so.outputs[j].Value) < 0
}

// Swap swaps two elements in the sortedOutputs set.
func (so sortedOutputs) Swap(i, j int) {
	so.ids[i], so.ids[j] = so.ids[j], so.ids[i]
	so.outputs[i], so.outputs[j] = so.outputs[j], so.outputs[i]
}
<|endoftext|>


<commit_before>// Copyright 2020 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package topdown

import (
	"fmt"
	"math/big"
	"math/rand"

	"github.com/open-policy-agent/opa/ast"
	"github.com/open-policy-agent/opa/topdown/builtins"
)

var one = big.NewInt(1)

func builtinNumbersRange(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {

	x, err := builtins.BigIntOperand(operands[0].Value, 1)
	if err != nil {
		return err
	}

	y, err := builtins.BigIntOperand(operands[1].Value, 2)
	if err != nil {
		return err
	}

	result := ast.NewArray()
	cmp := x.Cmp(y)
	haltErr := Halt{
		Err: &Error{
			Code:    CancelErr,
			Message: "numbers.range: timed out before generating all numbers in range",
		},
	}

	if cmp <= 0 {
		for i := new(big.Int).Set(x); i.Cmp(y) <= 0; i = i.Add(i, one) {
			if bctx.Cancel != nil && bctx.Cancel.Cancelled() {
				return haltErr
			}
			result = result.Append(ast.NewTerm(builtins.IntToNumber(i)))
		}
	} else {
		for i := new(big.Int).Set(x); i.Cmp(y) >= 0; i = i.Sub(i, one) {
			if bctx.Cancel != nil && bctx.Cancel.Cancelled() {
				return haltErr
			}
			result = result.Append(ast.NewTerm(builtins.IntToNumber(i)))
		}
	}

	return iter(ast.NewTerm(result))
}

func builtinRandIntn(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {

	strOp, err := builtins.StringOperand(args[0].Value, 1)
	if err != nil {
		return err

	}

	n, err := builtins.IntOperand(args[1].Value, 2)
	if err != nil {
		return err
	}

	var key = uuidCachingKey(fmt.Sprintf("%s-%d", strOp, n))
	var result *ast.Term

	if val, ok := bctx.Cache.Get(key); !ok {
		result = ast.IntNumberTerm(rand.Intn(n))
		bctx.Cache.Put(key, result)
	} else {
		result = val.(*ast.Term)
	}

	return iter(result)

}

func init() {
	RegisterBuiltinFunc(ast.NumbersRange.Name, builtinNumbersRange)
	RegisterBuiltinFunc(ast.RandIntn.Name, builtinRandIntn)
}
<commit_msg>topdown: Fix panic in rand.intn function<commit_after>// Copyright 2020 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package topdown

import (
	"fmt"
	"math/big"
	"math/rand"

	"github.com/open-policy-agent/opa/ast"
	"github.com/open-policy-agent/opa/topdown/builtins"
)

var one = big.NewInt(1)

func builtinNumbersRange(bctx BuiltinContext, operands []*ast.Term, iter func(*ast.Term) error) error {

	x, err := builtins.BigIntOperand(operands[0].Value, 1)
	if err != nil {
		return err
	}

	y, err := builtins.BigIntOperand(operands[1].Value, 2)
	if err != nil {
		return err
	}

	result := ast.NewArray()
	cmp := x.Cmp(y)
	haltErr := Halt{
		Err: &Error{
			Code:    CancelErr,
			Message: "numbers.range: timed out before generating all numbers in range",
		},
	}

	if cmp <= 0 {
		for i := new(big.Int).Set(x); i.Cmp(y) <= 0; i = i.Add(i, one) {
			if bctx.Cancel != nil && bctx.Cancel.Cancelled() {
				return haltErr
			}
			result = result.Append(ast.NewTerm(builtins.IntToNumber(i)))
		}
	} else {
		for i := new(big.Int).Set(x); i.Cmp(y) >= 0; i = i.Sub(i, one) {
			if bctx.Cancel != nil && bctx.Cancel.Cancelled() {
				return haltErr
			}
			result = result.Append(ast.NewTerm(builtins.IntToNumber(i)))
		}
	}

	return iter(ast.NewTerm(result))
}

func builtinRandIntn(bctx BuiltinContext, args []*ast.Term, iter func(*ast.Term) error) error {

	strOp, err := builtins.StringOperand(args[0].Value, 1)
	if err != nil {
		return err

	}

	n, err := builtins.IntOperand(args[1].Value, 2)
	if err != nil {
		return err
	} else if n <= 0 {
		return fmt.Errorf("invalid argument (n must be > 0)")
	}

	var key = uuidCachingKey(fmt.Sprintf("%s-%d", strOp, n))
	var result *ast.Term

	if val, ok := bctx.Cache.Get(key); !ok {
		result = ast.IntNumberTerm(rand.Intn(n))
		bctx.Cache.Put(key, result)
	} else {
		result = val.(*ast.Term)
	}

	return iter(result)

}

func init() {
	RegisterBuiltinFunc(ast.NumbersRange.Name, builtinNumbersRange)
	RegisterBuiltinFunc(ast.RandIntn.Name, builtinRandIntn)
}
<|endoftext|>


<commit_before>package monitor

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"syscall"
	"time"

	log "github.com/Sirupsen/logrus"
)

const (
	DEFAULT_CMD_TIMEOUT = time.Duration(3) * time.Second
)

type ExecMonitor struct {
	Base

	Timeout time.Duration
	FullCmd string
}

func NewExecMonitor(rmc *RootMonitorConfig) IMonitor {
	e := &ExecMonitor{
		Base: Base{
			RMC:        rmc,
			Identifier: "exec",
		},
	}

	// Set our cmd timeout
	if e.RMC.Config.Timeout.String() == "" {
		e.Timeout = DEFAULT_CMD_TIMEOUT
	} else {
		e.Timeout = time.Duration(e.RMC.Config.Timeout)
	}

	e.FullCmd = e.getFullCmd()
	e.MonitorFunc = e.execCheck

	return e
}

func (e *ExecMonitor) execCheck() error {
	log.Debugf("%v-%v: Performing check for '%v'", e.Identifier, e.RMC.GID, e.RMC.ConfigName)

	ctx, cancel := context.WithTimeout(context.Background(), e.Timeout)
	defer cancel()

	// TODO: This could use a refactor at some point
	output, err := exec.CommandContext(ctx, e.RMC.Config.ExecCommand, e.RMC.Config.ExecArgs...).CombinedOutput()
	if err != nil {
		// Did we timeout?
		if err.Error() == "signal: killed" {
			return fmt.Errorf("Command '%v' exceeded run timeout (%v)", e.FullCmd, e.RMC.Config.Timeout.String())
		}

		// Did we exit non-zero?
		if exiterr, ok := err.(*exec.ExitError); ok {
			// what return code did we get?
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				if status.ExitStatus() != e.RMC.Config.ExecReturnCode {
					return fmt.Errorf("Command '%v' exited with '%v', expected '%v'. Output: %v",
						e.FullCmd, status.ExitStatus(), e.RMC.Config.ExecReturnCode,
						strings.Replace(string(output), "\n", "\\n", -1))
				}
			} else {
				return fmt.Errorf("Unable to fetch return code for command '%v'. Full error: %v", e.FullCmd, err.Error())
			}
		} else {
			// Something else went bad
			return fmt.Errorf("Unexpected error during command '%v' execution: %v", e.FullCmd, err.Error())
		}
	} else {
		// Got a 0 return code; let's verify that we're okay with 0
		if e.RMC.Config.ExecReturnCode != 0 {
			return fmt.Errorf("Command '%v' exited with a '0' return code, expected '%v'", e.FullCmd, e.RMC.Config.ExecReturnCode)
		}
	}

	if e.RMC.Config.Expect == "" {
		return nil
	}

	if !strings.Contains(string(output), e.RMC.Config.Expect) {
		return fmt.Errorf("Command '%v' output does not contain expected output. Expected: %v Output: %v",
			e.FullCmd, e.RMC.Config.Expect, strings.Replace(string(output), "\n", "\\n", -1))
	}

	return nil
}

// Helper for displaying full cmd
func (e *ExecMonitor) getFullCmd() string {
	fullCmd := e.RMC.Config.ExecCommand

	for _, v := range e.RMC.Config.ExecArgs {
		fullCmd := fullCmd + " " + v
	}

	return fullCmd
}
<commit_msg>minor fix<commit_after>package monitor

import (
	"context"
	"fmt"
	"os/exec"
	"strings"
	"syscall"
	"time"

	log "github.com/Sirupsen/logrus"
)

const (
	DEFAULT_CMD_TIMEOUT = time.Duration(3) * time.Second
)

type ExecMonitor struct {
	Base

	Timeout time.Duration
	FullCmd string
}

func NewExecMonitor(rmc *RootMonitorConfig) IMonitor {
	e := &ExecMonitor{
		Base: Base{
			RMC:        rmc,
			Identifier: "exec",
		},
	}

	// Set our cmd timeout
	if e.RMC.Config.Timeout.String() == "" {
		e.Timeout = DEFAULT_CMD_TIMEOUT
	} else {
		e.Timeout = time.Duration(e.RMC.Config.Timeout)
	}

	e.FullCmd = e.getFullCmd()
	e.MonitorFunc = e.execCheck

	return e
}

func (e *ExecMonitor) execCheck() error {
	log.Debugf("%v-%v: Performing check for '%v'", e.Identifier, e.RMC.GID, e.RMC.ConfigName)

	ctx, cancel := context.WithTimeout(context.Background(), e.Timeout)
	defer cancel()

	// TODO: This could use a refactor at some point
	output, err := exec.CommandContext(ctx, e.RMC.Config.ExecCommand, e.RMC.Config.ExecArgs...).CombinedOutput()
	if err != nil {
		// Did we timeout?
		if err.Error() == "signal: killed" {
			return fmt.Errorf("Command '%v' exceeded run timeout (%v)", e.FullCmd, e.RMC.Config.Timeout.String())
		}

		// Did we exit non-zero?
		if exiterr, ok := err.(*exec.ExitError); ok {
			// what return code did we get?
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				if status.ExitStatus() != e.RMC.Config.ExecReturnCode {
					return fmt.Errorf("Command '%v' exited with '%v', expected '%v'. Output: %v",
						e.FullCmd, status.ExitStatus(), e.RMC.Config.ExecReturnCode,
						strings.Replace(string(output), "\n", "\\n", -1))
				}
			} else {
				return fmt.Errorf("Unable to fetch return code for command '%v'. Full error: %v", e.FullCmd, err.Error())
			}
		} else {
			// Something else went bad
			return fmt.Errorf("Unexpected error during command '%v' execution: %v", e.FullCmd, err.Error())
		}
	} else {
		// Got a 0 return code; let's verify that we're okay with 0
		if e.RMC.Config.ExecReturnCode != 0 {
			return fmt.Errorf("Command '%v' exited with a '0' return code, expected '%v'", e.FullCmd, e.RMC.Config.ExecReturnCode)
		}
	}

	if e.RMC.Config.Expect == "" {
		return nil
	}

	if !strings.Contains(string(output), e.RMC.Config.Expect) {
		return fmt.Errorf("Command '%v' output does not contain expected output. Expected: %v Output: %v",
			e.FullCmd, e.RMC.Config.Expect, strings.Replace(string(output), "\n", "\\n", -1))
	}

	return nil
}

// Helper for displaying full cmd
func (e *ExecMonitor) getFullCmd() string {
	fullCmd := e.RMC.Config.ExecCommand

	for _, v := range e.RMC.Config.ExecArgs {
		fullCmd = fullCmd + " " + v
	}

	return fullCmd
}
<|endoftext|>


<commit_before>package torrent

import (
	"github.com/cenkalti/rain/internal/webseeddownloader"
)

func (t *torrent) setWebseedDownloader() {
	if len(t.webseedSources) > 0 {
		t.webseedDownloader = webseeddownloader.New(t.webseedClient, t.webseedSources, t.bitfield)
	}
}
<commit_msg>handle webseed piece result<commit_after>package torrent

import (
	"github.com/cenkalti/rain/internal/piecewriter"
	"github.com/cenkalti/rain/internal/urldownloader"
)

func (t *torrent) handleWebseedPieceResult(msg *urldownloader.PieceResult) {
	if msg.Error != nil {
		t.log.Errorln("webseed download error:", msg.Error)
		// TODO handle error
		return
	}

	t.log.Debugln("piece download completed. index:", msg.Index)

	piece := &t.pieces[msg.Index]

	t.resumerStats.BytesDownloaded += int64(len(msg.Buffer.Data))

	// TODO update download speed
	// t.downloadSpeed.Update(int64(len(msg.Buffer.Data)))

	if piece.Writing {
		panic("piece is already writing")
	}
	piece.Writing = true

	// TODO suspend webseed piece result chan
	// TODO suspend webseed piece result chan on peer message
	// Prevent receiving piece messages to avoid more than 1 write per torrent.
	t.pieceMessagesC.Suspend()

	pw := piecewriter.New(piece, msg.Downloader, msg.Buffer)
	go pw.Run(t.pieceWriterResultC, t.doneC)
}
<|endoftext|>


<commit_before>package snickers_test

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"

	"github.com/gorilla/mux"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/rest"
	"github.com/snickers/snickers/types"
)

var _ = Describe("Rest API", func() {
	var (
		response   *httptest.ResponseRecorder
		server     *mux.Router
		dbInstance db.DatabaseInterface
	)

	BeforeEach(func() {
		response = httptest.NewRecorder()
		server = rest.NewRouter()
		dbInstance, _ = db.GetDatabase()
		dbInstance.ClearDatabase()
	})

	Describe("GET /jobs", func() {
		It("should return application/json on its content type", func() {
			request, _ := http.NewRequest("GET", "/jobs", nil)
			server.ServeHTTP(response, request)
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
		})

		It("should return stored jobs", func() {
			exampleJob1 := types.Job{ID: "123"}
			exampleJob2 := types.Job{ID: "321"}
			dbInstance.StoreJob(exampleJob1)
			dbInstance.StoreJob(exampleJob2)

			expected1, _ := json.Marshal(`[{"id":"123","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""},{"id":"321","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""}]`)
			expected2, _ := json.Marshal(`[{"id":"321","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""},{"id":"123","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""}]`)

			request, _ := http.NewRequest("GET", "/jobs", nil)
			server.ServeHTTP(response, request)
			responseBody, _ := json.Marshal(response.Body.String())

			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(responseBody).To(SatisfyAny(Equal(expected1), Equal(expected2)))
		})
	})

	Describe("POST /jobs", func() {
		It("should create a new job", func() {
			dbInstance.StorePreset(types.Preset{Name: "presetName"})
			jobJSON := []byte(`{"source": "http://flv.io/src.mp4", "destination": "s3://l@p:google.com", "preset": "presetName"}`)
			request, _ := http.NewRequest("POST", "/jobs", bytes.NewBuffer(jobJSON))
			server.ServeHTTP(response, request)

			jobs, _ := dbInstance.GetJobs()
			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			Expect(len(jobs)).To(Equal(1))
			job := jobs[0]
			Expect(job.Source).To(Equal("http://flv.io/src.mp4"))
			Expect(job.Destination).To(Equal("s3://l@p:google.com"))
			Expect(job.Preset.Name).To(Equal("presetName"))
		})

		Context("when the request is invalid", func() {
			It("should return BadRequest if preset is not set", func() {
				jobJSON := []byte(`{"source": "http://flv.io/src.mp4", "destination": "s3://l@p:google.com", "preset": "presetName"}`)
				request, _ := http.NewRequest("POST", "/jobs", bytes.NewBuffer(jobJSON))
				server.ServeHTTP(response, request)
				responseBody, _ := json.Marshal(response.Body.String())

				expected, _ := json.Marshal(`{"error": "retrieving preset: preset not found"}`)
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})

			It("should return BadRequest if preset is malformed", func() {
				jobJSON := []byte(`{"source: "http://flv.io/src.mp4", "destinat}`)
				request, _ := http.NewRequest("POST", "/jobs", bytes.NewBuffer(jobJSON))
				server.ServeHTTP(response, request)
				responseBody, _ := json.Marshal(response.Body.String())

				expected, _ := json.Marshal(`{"error": "unpacking job: invalid character 'h' after object key"}`)
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})
		})
	})

	Describe("GET /job/:id", func() {
		It("should return the job with details", func() {
			job := types.Job{
				ID:          "123-123-123",
				Source:      "http://source.here.mp4",
				Destination: "s3://ae@ae.com",
				Preset:      types.Preset{},
				Status:      types.JobCreated,
				Details:     "0%",
			}
			dbInstance.StoreJob(job)
			expected, _ := json.Marshal(&job)

			request, _ := http.NewRequest("GET", "/jobs/123-123-123", nil)
			server.ServeHTTP(response, request)
			Expect(response.Body.String()).To(Equal(string(expected)))
			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
		})

		Context("when getting the job fails", func() {
			It("should return BadRequest", func() {
				request, _ := http.NewRequest("GET", "/jobs/123-456-9292", nil)
				server.ServeHTTP(response, request)
				expected, _ := json.Marshal(`{"error": "retrieving job: job not found"}`)
				responseBody, _ := json.Marshal(response.Body.String())
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})
		})
	})

	Describe("POST /jobs/:id/start", func() {
		It("should return status OK", func() {
			job := types.Job{
				ID:          "123-123-123",
				Source:      "http://source.here.mp4",
				Destination: "s3://ae@ae.com",
				Preset:      types.Preset{},
				Status:      types.JobCreated,
				Details:     "0%",
			}
			dbInstance.StoreJob(job)

			request, _ := http.NewRequest("POST", "/jobs/123-123-123/start", nil)
			server.ServeHTTP(response, request)
			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
		})

		Context("when starting a job fails", func() {
			It("should return BadRequest", func() {
				request, _ := http.NewRequest("POST", "/jobs/123-456-9292/start", nil)
				server.ServeHTTP(response, request)
				expected, _ := json.Marshal(`{"error": "retrieving job: job not found"}`)
				responseBody, _ := json.Marshal(response.Body.String())
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})
		})
	})
})
<commit_msg>test job creation contains id, status, progress<commit_after>package snickers_test

import (
	"bytes"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"

	"github.com/gorilla/mux"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/snickers/snickers/db"
	"github.com/snickers/snickers/rest"
	"github.com/snickers/snickers/types"
)

var _ = Describe("Rest API", func() {
	var (
		response   *httptest.ResponseRecorder
		server     *mux.Router
		dbInstance db.DatabaseInterface
	)

	BeforeEach(func() {
		response = httptest.NewRecorder()
		server = rest.NewRouter()
		dbInstance, _ = db.GetDatabase()
		dbInstance.ClearDatabase()
	})

	Describe("GET /jobs", func() {
		It("should return application/json on its content type", func() {
			request, _ := http.NewRequest("GET", "/jobs", nil)
			server.ServeHTTP(response, request)
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
		})

		It("should return stored jobs", func() {
			exampleJob1 := types.Job{ID: "123"}
			exampleJob2 := types.Job{ID: "321"}
			dbInstance.StoreJob(exampleJob1)
			dbInstance.StoreJob(exampleJob2)

			expected1, _ := json.Marshal(`[{"id":"123","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""},{"id":"321","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""}]`)
			expected2, _ := json.Marshal(`[{"id":"321","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""},{"id":"123","source":"","destination":"","preset":{"video":{},"audio":{}},"status":"","progress":""}]`)

			request, _ := http.NewRequest("GET", "/jobs", nil)
			server.ServeHTTP(response, request)
			responseBody, _ := json.Marshal(response.Body.String())

			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(responseBody).To(SatisfyAny(Equal(expected1), Equal(expected2)))
		})
	})

	Describe("POST /jobs", func() {
		It("should create a new job", func() {
			dbInstance.StorePreset(types.Preset{Name: "presetName"})
			jobJSON := []byte(`{"source": "http://flv.io/src.mp4", "destination": "s3://l@p:google.com", "preset": "presetName"}`)
			request, _ := http.NewRequest("POST", "/jobs", bytes.NewBuffer(jobJSON))
			server.ServeHTTP(response, request)
			responseBody := response.Body.String()

			jobs, _ := dbInstance.GetJobs()
			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			Expect(len(jobs)).To(Equal(1))
			Expect(strings.Contains(responseBody, "id")).To(BeTrue())
			Expect(strings.Contains(responseBody, `"status":"created"`)).To(BeTrue())
			Expect(strings.Contains(responseBody, `"progress":""`)).To(BeTrue())

			job := jobs[0]
			Expect(job.Source).To(Equal("http://flv.io/src.mp4"))
			Expect(job.Destination).To(Equal("s3://l@p:google.com"))
			Expect(job.Preset.Name).To(Equal("presetName"))
		})

		Context("when the request is invalid", func() {
			It("should return BadRequest if preset is not set", func() {
				jobJSON := []byte(`{"source": "http://flv.io/src.mp4", "destination": "s3://l@p:google.com", "preset": "presetName"}`)
				request, _ := http.NewRequest("POST", "/jobs", bytes.NewBuffer(jobJSON))
				server.ServeHTTP(response, request)
				responseBody, _ := json.Marshal(response.Body.String())

				expected, _ := json.Marshal(`{"error": "retrieving preset: preset not found"}`)
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})

			It("should return BadRequest if preset is malformed", func() {
				jobJSON := []byte(`{"source: "http://flv.io/src.mp4", "destinat}`)
				request, _ := http.NewRequest("POST", "/jobs", bytes.NewBuffer(jobJSON))
				server.ServeHTTP(response, request)
				responseBody, _ := json.Marshal(response.Body.String())

				expected, _ := json.Marshal(`{"error": "unpacking job: invalid character 'h' after object key"}`)
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})
		})
	})

	Describe("GET /job/:id", func() {
		It("should return the job with details", func() {
			job := types.Job{
				ID:          "123-123-123",
				Source:      "http://source.here.mp4",
				Destination: "s3://ae@ae.com",
				Preset:      types.Preset{},
				Status:      types.JobCreated,
				Details:     "0%",
			}
			dbInstance.StoreJob(job)
			expected, _ := json.Marshal(&job)

			request, _ := http.NewRequest("GET", "/jobs/123-123-123", nil)
			server.ServeHTTP(response, request)
			Expect(response.Body.String()).To(Equal(string(expected)))
			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
		})

		Context("when getting the job fails", func() {
			It("should return BadRequest", func() {
				request, _ := http.NewRequest("GET", "/jobs/123-456-9292", nil)
				server.ServeHTTP(response, request)
				expected, _ := json.Marshal(`{"error": "retrieving job: job not found"}`)
				responseBody, _ := json.Marshal(response.Body.String())
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})
		})
	})

	Describe("POST /jobs/:id/start", func() {
		It("should return status OK", func() {
			job := types.Job{
				ID:          "123-123-123",
				Source:      "http://source.here.mp4",
				Destination: "s3://ae@ae.com",
				Preset:      types.Preset{},
				Status:      types.JobCreated,
				Details:     "0%",
			}
			dbInstance.StoreJob(job)

			request, _ := http.NewRequest("POST", "/jobs/123-123-123/start", nil)
			server.ServeHTTP(response, request)
			Expect(response.Code).To(Equal(http.StatusOK))
			Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
		})

		Context("when starting a job fails", func() {
			It("should return BadRequest", func() {
				request, _ := http.NewRequest("POST", "/jobs/123-456-9292/start", nil)
				server.ServeHTTP(response, request)
				expected, _ := json.Marshal(`{"error": "retrieving job: job not found"}`)
				responseBody, _ := json.Marshal(response.Body.String())
				Expect(responseBody).To(Equal(expected))
				Expect(response.Code).To(Equal(http.StatusBadRequest))
				Expect(response.HeaderMap["Content-Type"][0]).To(Equal("application/json; charset=UTF-8"))
			})
		})
	})
})
<|endoftext|>