<commit_before>// Copyright 2015 The Vanadium 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 vom

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"testing"

	"v.io/v23/vdl"
	"v.io/v23/vom/testdata"
)

func TestDecoder(t *testing.T) {
	for _, test := range testdata.Tests {
		// Decode hex patterns into binary data.
		binversion, err := binFromHexPat(test.HexVersion)
		if err != nil {
			t.Errorf("%s: couldn't convert to binary from hexversion: %q", test.Name, test.HexVersion)
			continue
		}
		bintype, err := binFromHexPat(test.HexType)
		if err != nil {
			t.Errorf("%s: couldn't convert to binary from hextype: %q", test.Name, test.HexType)
			continue
		}
		binvalue, err := binFromHexPat(test.HexValue)
		if err != nil {
			t.Errorf("%s: couldn't convert to binary from hexvalue: %q", test.Name, test.HexValue)
			continue
		}

		name := test.Name + " [vdl.Value]"
		testDecodeVDL(t, name, binversion+bintype+binvalue, test.Value)
		name = test.Name + " [vdl.Value] (with TypeDecoder)"
		testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, test.Value)

		name = test.Name + " [vdl.Any]"
		testDecodeVDL(t, name, binversion+bintype+binvalue, vdl.AnyValue(test.Value))
		name = test.Name + " [vdl.Any] (with TypeDecoder)"
		testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, vdl.AnyValue(test.Value))

		// Convert into Go value for the rest of our tests.
		goValue, err := toGoValue(test.Value)
		if err != nil {
			t.Errorf("%s: %v", test.Name, err)
			continue
		}

		name = test.Name + " [go value]"
		testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf(goValue), goValue)
		name = test.Name + " [go value] (with TypeDecoder)"
		testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf(goValue), goValue)

		name = test.Name + " [go interface]"
		testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), goValue)
		name = test.Name + " [go interface] (with TypeDecoder)"
		testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), goValue)
	}
}

func testDecodeVDL(t *testing.T, name, bin string, value *vdl.Value) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		decoder := NewDecoder(mode.testReader(strings.NewReader(bin)))
		got := vdl.ZeroValue(value.Type())
		if err := decoder.Decode(got); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if want := value; !vdl.EqualValue(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
			return
		}
	}
	// Test single-shot vom.Decode twice, to ensure we test the cache hit case.
	testDecodeVDLSingleShot(t, name, bin, value)
	testDecodeVDLSingleShot(t, name, bin, value)
}

func testDecodeVDLSingleShot(t *testing.T, name, bin string, value *vdl.Value) {
	// Test the single-shot vom.Decode.
	head := fmt.Sprintf("%s (single-shot)", name)
	got := vdl.ZeroValue(value.Type())
	if err := Decode([]byte(bin), got); err != nil {
		t.Errorf("%s: Decode failed: %v", head, err)
		return
	}
	if want := value; !vdl.EqualValue(got, want) {
		t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
		return
	}
}

func testDecodeVDLWithTypeDecoder(t *testing.T, name, binversion, bintype, binvalue string, value *vdl.Value) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		typedec := NewTypeDecoder(mode.testReader(strings.NewReader(binversion + bintype)))
		decoder := NewDecoderWithTypeDecoder(mode.testReader(strings.NewReader(binversion+binvalue)), typedec)
		got := vdl.ZeroValue(value.Type())
		if err := decoder.Decode(got); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if want := value; !vdl.EqualValue(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
			return
		}
	}
}

func testDecodeGo(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		decoder := NewDecoder(mode.testReader(strings.NewReader(bin)))
		rvGot := reflect.New(rt)
		if err := decoder.Decode(rvGot.Interface()); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if got := rvGot.Elem().Interface(); !reflect.DeepEqual(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
			return
		}
	}
	// Test single-shot vom.Decode twice, to ensure we test the cache hit case.
	testDecodeGoSingleShot(t, name, bin, rt, want)
	testDecodeGoSingleShot(t, name, bin, rt, want)
}

func testDecodeGoSingleShot(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
	head := fmt.Sprintf("%s (single-shot)", name)
	rvGot := reflect.New(rt)
	if err := Decode([]byte(bin), rvGot.Interface()); err != nil {
		t.Errorf("%s: Decode failed: %v", head, err)
		return
	}
	if got := rvGot.Elem().Interface(); !reflect.DeepEqual(got, want) {
		t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
		return
	}
}

func testDecodeGoWithTypeDecoder(t *testing.T, name, binversion, bintype, binvalue string, rt reflect.Type, want interface{}) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		typedec := NewTypeDecoder(mode.testReader(strings.NewReader(binversion + bintype)))
		decoder := NewDecoderWithTypeDecoder(mode.testReader(strings.NewReader(binversion+binvalue)), typedec)
		rv := reflect.New(rt)
		if err := decoder.Decode(rv.Interface()); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if got := rv.Elem().Interface(); !reflect.DeepEqual(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
			return
		}
	}
}

// In concurrent modes, one goroutine may try to read vom types before they are
// actually sent by other goroutine. We use a simple buffered pipe to provide
// blocking read since bytes.Buffer will return EOF in this case.
type pipe struct {
	b bytes.Buffer
	m sync.Mutex
	c sync.Cond
}

func newPipe() (io.Reader, io.Writer) {
	p := &pipe{}
	p.c.L = &p.m
	return p, p
}

func (r *pipe) Read(p []byte) (n int, err error) {
	r.m.Lock()
	defer r.m.Unlock()
	for r.b.Len() == 0 {
		r.c.Wait()
	}
	return r.b.Read(p)
}

func (w *pipe) Write(p []byte) (n int, err error) {
	w.m.Lock()
	defer w.m.Unlock()
	defer w.c.Signal()
	return w.b.Write(p)
}

// TestRoundtrip* tests test encoding and then decoding results in various modes.
func TestRoundtrip(t *testing.T)                   { testRoundtrip(t, false, 1) }
func TestRoundtripWithTypeDecoder_1(t *testing.T)  { testRoundtrip(t, true, 1) }
func TestRoundtripWithTypeDecoder_5(t *testing.T)  { testRoundtrip(t, true, 5) }
func TestRoundtripWithTypeDecoder_10(t *testing.T) { testRoundtrip(t, true, 10) }
func TestRoundtripWithTypeDecoder_20(t *testing.T) { testRoundtrip(t, true, 20) }

func testRoundtrip(t *testing.T, withTypeEncoderDecoder bool, concurrency int) {
	mp := runtime.GOMAXPROCS(runtime.NumCPU())
	defer runtime.GOMAXPROCS(mp)

	tests := []struct {
		In, Want interface{}
	}{
		// Test that encoding nil/empty composites leads to nil.
		{[]byte(nil), []byte(nil)},
		{[]byte{}, []byte(nil)},
		{[]int64(nil), []int64(nil)},
		{[]int64{}, []int64(nil)},
		{map[string]int64(nil), map[string]int64(nil)},
		{map[string]int64{}, map[string]int64(nil)},
		{struct{}{}, struct{}{}},
		{struct{ A []byte }{nil}, struct{ A []byte }{}},
		{struct{ A []byte }{[]byte{}}, struct{ A []byte }{}},
		{struct{ A []int64 }{nil}, struct{ A []int64 }{}},
		{struct{ A []int64 }{[]int64{}}, struct{ A []int64 }{}},
		// Test that encoding nil typeobject leads to AnyType.
		{(*vdl.Type)(nil), vdl.AnyType},
		// Test that both encoding and decoding ignore unexported fields.
		{struct{ a, X, b string }{"a", "XYZ", "b"}, struct{ d, X, e string }{X: "XYZ"}},
		{
			struct {
				a bool
				X string
				b int64
			}{true, "XYZ", 123},
			struct {
				a complex64
				X string
				b []byte
			}{X: "XYZ"},
		},
		// Test for array encoding/decoding.
		{[3]byte{1, 2, 3}, [3]byte{1, 2, 3}},
		{[3]int64{1, 2, 3}, [3]int64{1, 2, 3}},
		// Test for zero value struct/union field encoding/decoding.
		{struct{ A int64 }{0}, struct{ A int64 }{}},
		{struct{ T *vdl.Type }{nil}, struct{ T *vdl.Type }{vdl.AnyType}},
		{struct{ M map[uint64]struct{} }{make(map[uint64]struct{})}, struct{ M map[uint64]struct{} }{}},
		{struct{ M map[uint64]string }{make(map[uint64]string)}, struct{ M map[uint64]string }{}},
		{struct{ N struct{ A int64 } }{struct{ A int64 }{0}}, struct{ N struct{ A int64 } }{}},
		{struct{ N *testdata.NStruct }{&testdata.NStruct{false, "", 0}}, struct{ N *testdata.NStruct }{&testdata.NStruct{}}},
		{struct{ N *testdata.NStruct }{nil}, struct{ N *testdata.NStruct }{}},
		{testdata.NUnion(testdata.NUnionA{false}), testdata.NUnion(testdata.NUnionA{})},
		{testdata.RecA{}, testdata.RecA(nil)},
		{testdata.RecStruct{}, testdata.RecStruct{}},
		// Test for verifying correctness when encoding/decoding shared types concurrently.
		{testdata.MStruct{}, testdata.MStruct{E: vdl.AnyType, F: vdl.AnyValue(nil)}},
		{testdata.NStruct{}, testdata.NStruct{}},
		{testdata.XyzStruct{}, testdata.XyzStruct{}},
		{testdata.YzStruct{}, testdata.YzStruct{}},
		{testdata.MBool(false), testdata.MBool(false)},
		{testdata.NString(""), testdata.NString("")},
	}

	var (
		typeenc *TypeEncoder
		typedec *TypeDecoder
	)
	if withTypeEncoderDecoder {
		r, w := newPipe()
		typeenc = NewTypeEncoder(w)
		typedec = NewTypeDecoder(r)
	}

	var wg sync.WaitGroup
	for n := 0; n < concurrency; n++ {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			for _, n := range rand.Perm(len(tests) * 10) {
				test := tests[n%len(tests)]
				name := fmt.Sprintf("[%d]:%#v,%#v", n, test.In, test.Want)

				var (
					encoder *Encoder
					decoder *Decoder
					buf     bytes.Buffer
				)
				if withTypeEncoderDecoder {
					encoder = NewEncoderWithTypeEncoder(&buf, typeenc)
					decoder = NewDecoderWithTypeDecoder(&buf, typedec)
				} else {
					encoder = NewEncoder(&buf)
					decoder = NewDecoder(&buf)
				}

				if err := encoder.Encode(test.In); err != nil {
					t.Errorf("%s: Encode(%#v) failed: %v", name, test.In, err)
					return
				}
				rv := reflect.New(reflect.TypeOf(test.Want))
				if err := decoder.Decode(rv.Interface()); err != nil {
					t.Errorf("%s: Decode failed: %v", name, err)
					return
				}
				if got := rv.Elem().Interface(); !reflect.DeepEqual(got, test.Want) {
					t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", name, got, got, test.Want, test.Want)
					return
				}
			}
		}(n)
	}
	wg.Wait()
}
<commit_msg>v23: fixing last couple of go vet violations<commit_after>// Copyright 2015 The Vanadium 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 vom

import (
	"bytes"
	"fmt"
	"io"
	"math/rand"
	"reflect"
	"runtime"
	"strings"
	"sync"
	"testing"

	"v.io/v23/vdl"
	"v.io/v23/vom/testdata"
)

func TestDecoder(t *testing.T) {
	for _, test := range testdata.Tests {
		// Decode hex patterns into binary data.
		binversion, err := binFromHexPat(test.HexVersion)
		if err != nil {
			t.Errorf("%s: couldn't convert to binary from hexversion: %q", test.Name, test.HexVersion)
			continue
		}
		bintype, err := binFromHexPat(test.HexType)
		if err != nil {
			t.Errorf("%s: couldn't convert to binary from hextype: %q", test.Name, test.HexType)
			continue
		}
		binvalue, err := binFromHexPat(test.HexValue)
		if err != nil {
			t.Errorf("%s: couldn't convert to binary from hexvalue: %q", test.Name, test.HexValue)
			continue
		}

		name := test.Name + " [vdl.Value]"
		testDecodeVDL(t, name, binversion+bintype+binvalue, test.Value)
		name = test.Name + " [vdl.Value] (with TypeDecoder)"
		testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, test.Value)

		name = test.Name + " [vdl.Any]"
		testDecodeVDL(t, name, binversion+bintype+binvalue, vdl.AnyValue(test.Value))
		name = test.Name + " [vdl.Any] (with TypeDecoder)"
		testDecodeVDLWithTypeDecoder(t, name, binversion, bintype, binvalue, vdl.AnyValue(test.Value))

		// Convert into Go value for the rest of our tests.
		goValue, err := toGoValue(test.Value)
		if err != nil {
			t.Errorf("%s: %v", test.Name, err)
			continue
		}

		name = test.Name + " [go value]"
		testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf(goValue), goValue)
		name = test.Name + " [go value] (with TypeDecoder)"
		testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf(goValue), goValue)

		name = test.Name + " [go interface]"
		testDecodeGo(t, name, binversion+bintype+binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), goValue)
		name = test.Name + " [go interface] (with TypeDecoder)"
		testDecodeGoWithTypeDecoder(t, name, binversion, bintype, binvalue, reflect.TypeOf((*interface{})(nil)).Elem(), goValue)
	}
}

func testDecodeVDL(t *testing.T, name, bin string, value *vdl.Value) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		decoder := NewDecoder(mode.testReader(strings.NewReader(bin)))
		got := vdl.ZeroValue(value.Type())
		if err := decoder.Decode(got); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if want := value; !vdl.EqualValue(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
			return
		}
	}
	// Test single-shot vom.Decode twice, to ensure we test the cache hit case.
	testDecodeVDLSingleShot(t, name, bin, value)
	testDecodeVDLSingleShot(t, name, bin, value)
}

func testDecodeVDLSingleShot(t *testing.T, name, bin string, value *vdl.Value) {
	// Test the single-shot vom.Decode.
	head := fmt.Sprintf("%s (single-shot)", name)
	got := vdl.ZeroValue(value.Type())
	if err := Decode([]byte(bin), got); err != nil {
		t.Errorf("%s: Decode failed: %v", head, err)
		return
	}
	if want := value; !vdl.EqualValue(got, want) {
		t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
		return
	}
}

func testDecodeVDLWithTypeDecoder(t *testing.T, name, binversion, bintype, binvalue string, value *vdl.Value) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		typedec := NewTypeDecoder(mode.testReader(strings.NewReader(binversion + bintype)))
		decoder := NewDecoderWithTypeDecoder(mode.testReader(strings.NewReader(binversion+binvalue)), typedec)
		got := vdl.ZeroValue(value.Type())
		if err := decoder.Decode(got); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if want := value; !vdl.EqualValue(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %v\nWANT %v", head, got, want)
			return
		}
	}
}

func testDecodeGo(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		decoder := NewDecoder(mode.testReader(strings.NewReader(bin)))
		rvGot := reflect.New(rt)
		if err := decoder.Decode(rvGot.Interface()); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if got := rvGot.Elem().Interface(); !reflect.DeepEqual(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
			return
		}
	}
	// Test single-shot vom.Decode twice, to ensure we test the cache hit case.
	testDecodeGoSingleShot(t, name, bin, rt, want)
	testDecodeGoSingleShot(t, name, bin, rt, want)
}

func testDecodeGoSingleShot(t *testing.T, name, bin string, rt reflect.Type, want interface{}) {
	head := fmt.Sprintf("%s (single-shot)", name)
	rvGot := reflect.New(rt)
	if err := Decode([]byte(bin), rvGot.Interface()); err != nil {
		t.Errorf("%s: Decode failed: %v", head, err)
		return
	}
	if got := rvGot.Elem().Interface(); !reflect.DeepEqual(got, want) {
		t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
		return
	}
}

func testDecodeGoWithTypeDecoder(t *testing.T, name, binversion, bintype, binvalue string, rt reflect.Type, want interface{}) {
	for _, mode := range allReadModes {
		head := fmt.Sprintf("%s (%s)", name, mode)
		typedec := NewTypeDecoder(mode.testReader(strings.NewReader(binversion + bintype)))
		decoder := NewDecoderWithTypeDecoder(mode.testReader(strings.NewReader(binversion+binvalue)), typedec)
		rv := reflect.New(rt)
		if err := decoder.Decode(rv.Interface()); err != nil {
			t.Errorf("%s: Decode failed: %v", head, err)
			return
		}
		if got := rv.Elem().Interface(); !reflect.DeepEqual(got, want) {
			t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", head, got, got, want, want)
			return
		}
	}
}

// In concurrent modes, one goroutine may try to read vom types before they are
// actually sent by other goroutine. We use a simple buffered pipe to provide
// blocking read since bytes.Buffer will return EOF in this case.
type pipe struct {
	b bytes.Buffer
	m sync.Mutex
	c sync.Cond
}

func newPipe() (io.Reader, io.Writer) {
	p := &pipe{}
	p.c.L = &p.m
	return p, p
}

func (r *pipe) Read(p []byte) (n int, err error) {
	r.m.Lock()
	defer r.m.Unlock()
	for r.b.Len() == 0 {
		r.c.Wait()
	}
	return r.b.Read(p)
}

func (w *pipe) Write(p []byte) (n int, err error) {
	w.m.Lock()
	defer w.m.Unlock()
	defer w.c.Signal()
	return w.b.Write(p)
}

// TestRoundtrip* tests test encoding and then decoding results in various modes.
func TestRoundtrip(t *testing.T)                   { testRoundtrip(t, false, 1) }
func TestRoundtripWithTypeDecoder_1(t *testing.T)  { testRoundtrip(t, true, 1) }
func TestRoundtripWithTypeDecoder_5(t *testing.T)  { testRoundtrip(t, true, 5) }
func TestRoundtripWithTypeDecoder_10(t *testing.T) { testRoundtrip(t, true, 10) }
func TestRoundtripWithTypeDecoder_20(t *testing.T) { testRoundtrip(t, true, 20) }

func testRoundtrip(t *testing.T, withTypeEncoderDecoder bool, concurrency int) {
	mp := runtime.GOMAXPROCS(runtime.NumCPU())
	defer runtime.GOMAXPROCS(mp)

	tests := []struct {
		In, Want interface{}
	}{
		// Test that encoding nil/empty composites leads to nil.
		{[]byte(nil), []byte(nil)},
		{[]byte{}, []byte(nil)},
		{[]int64(nil), []int64(nil)},
		{[]int64{}, []int64(nil)},
		{map[string]int64(nil), map[string]int64(nil)},
		{map[string]int64{}, map[string]int64(nil)},
		{struct{}{}, struct{}{}},
		{struct{ A []byte }{nil}, struct{ A []byte }{}},
		{struct{ A []byte }{[]byte{}}, struct{ A []byte }{}},
		{struct{ A []int64 }{nil}, struct{ A []int64 }{}},
		{struct{ A []int64 }{[]int64{}}, struct{ A []int64 }{}},
		// Test that encoding nil typeobject leads to AnyType.
		{(*vdl.Type)(nil), vdl.AnyType},
		// Test that both encoding and decoding ignore unexported fields.
		{struct{ a, X, b string }{"a", "XYZ", "b"}, struct{ d, X, e string }{X: "XYZ"}},
		{
			struct {
				a bool
				X string
				b int64
			}{true, "XYZ", 123},
			struct {
				a complex64
				X string
				b []byte
			}{X: "XYZ"},
		},
		// Test for array encoding/decoding.
		{[3]byte{1, 2, 3}, [3]byte{1, 2, 3}},
		{[3]int64{1, 2, 3}, [3]int64{1, 2, 3}},
		// Test for zero value struct/union field encoding/decoding.
		{struct{ A int64 }{0}, struct{ A int64 }{}},
		{struct{ T *vdl.Type }{nil}, struct{ T *vdl.Type }{vdl.AnyType}},
		{struct{ M map[uint64]struct{} }{make(map[uint64]struct{})}, struct{ M map[uint64]struct{} }{}},
		{struct{ M map[uint64]string }{make(map[uint64]string)}, struct{ M map[uint64]string }{}},
		{struct{ N struct{ A int64 } }{struct{ A int64 }{0}}, struct{ N struct{ A int64 } }{}},
		{struct{ N *testdata.NStruct }{&testdata.NStruct{A: false, B: "", C: 0}}, struct{ N *testdata.NStruct }{&testdata.NStruct{}}},
		{struct{ N *testdata.NStruct }{nil}, struct{ N *testdata.NStruct }{}},
		{testdata.NUnion(testdata.NUnionA{Value: false}), testdata.NUnion(testdata.NUnionA{})},
		{testdata.RecA{}, testdata.RecA(nil)},
		{testdata.RecStruct{}, testdata.RecStruct{}},
		// Test for verifying correctness when encoding/decoding shared types concurrently.
		{testdata.MStruct{}, testdata.MStruct{E: vdl.AnyType, F: vdl.AnyValue(nil)}},
		{testdata.NStruct{}, testdata.NStruct{}},
		{testdata.XyzStruct{}, testdata.XyzStruct{}},
		{testdata.YzStruct{}, testdata.YzStruct{}},
		{testdata.MBool(false), testdata.MBool(false)},
		{testdata.NString(""), testdata.NString("")},
	}

	var (
		typeenc *TypeEncoder
		typedec *TypeDecoder
	)
	if withTypeEncoderDecoder {
		r, w := newPipe()
		typeenc = NewTypeEncoder(w)
		typedec = NewTypeDecoder(r)
	}

	var wg sync.WaitGroup
	for n := 0; n < concurrency; n++ {
		wg.Add(1)
		go func(n int) {
			defer wg.Done()
			for _, n := range rand.Perm(len(tests) * 10) {
				test := tests[n%len(tests)]
				name := fmt.Sprintf("[%d]:%#v,%#v", n, test.In, test.Want)

				var (
					encoder *Encoder
					decoder *Decoder
					buf     bytes.Buffer
				)
				if withTypeEncoderDecoder {
					encoder = NewEncoderWithTypeEncoder(&buf, typeenc)
					decoder = NewDecoderWithTypeDecoder(&buf, typedec)
				} else {
					encoder = NewEncoder(&buf)
					decoder = NewDecoder(&buf)
				}

				if err := encoder.Encode(test.In); err != nil {
					t.Errorf("%s: Encode(%#v) failed: %v", name, test.In, err)
					return
				}
				rv := reflect.New(reflect.TypeOf(test.Want))
				if err := decoder.Decode(rv.Interface()); err != nil {
					t.Errorf("%s: Decode failed: %v", name, err)
					return
				}
				if got := rv.Elem().Interface(); !reflect.DeepEqual(got, test.Want) {
					t.Errorf("%s: Decode mismatch\nGOT %T %#v\nWANT %T %#v", name, got, got, test.Want, test.Want)
					return
				}
			}
		}(n)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package lfs

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/github/git-lfs/git"
)

var (
	valueRegexp           = regexp.MustCompile("\\Agit[\\-\\s]media")
	NotInARepositoryError = errors.New("Not in a repository")

	prePushHook     = "#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 0; }\ngit lfs pre-push \"$@\""
	prePushUpgrades = map[string]bool{
		"#!/bin/sh\ngit lfs push --stdin $*":     true,
		"#!/bin/sh\ngit lfs push --stdin \"$@\"": true,
		"#!/bin/sh\ngit lfs pre-push \"$@\"":     true,
	}
)

type HookExists struct {
	Name     string
	Path     string
	Contents string
}

func (e *HookExists) Error() string {
	return fmt.Sprintf("Hook already exists: %s\n\n%s\n", e.Name, e.Contents)
}

func InstallHooks(force bool) error {
	if !InRepo() {
		return NotInARepositoryError
	}

	if err := os.MkdirAll(filepath.Join(LocalGitDir, "hooks"), 0755); err != nil {
		return err
	}

	hookPath := filepath.Join(LocalGitDir, "hooks", "pre-push")
	if _, err := os.Stat(hookPath); err == nil && !force {
		return upgradeHookOrError(hookPath, "pre-push", prePushHook, prePushUpgrades)
	}

	return ioutil.WriteFile(hookPath, []byte(prePushHook+"\n"), 0755)
}

func upgradeHookOrError(hookPath, hookName, hook string, upgrades map[string]bool) error {
	file, err := os.Open(hookPath)
	if err != nil {
		return err
	}

	by, err := ioutil.ReadAll(io.LimitReader(file, 1024))
	file.Close()
	if err != nil {
		return err
	}

	contents := strings.TrimSpace(string(by))
	if contents == hook {
		return nil
	}

	if upgrades[contents] {
		return ioutil.WriteFile(hookPath, []byte(hook+"\n"), 0755)
	}

	return &HookExists{hookName, hookPath, contents}
}

func InstallFilters() error {
	if err := setFilter("clean"); err != nil {
		return err
	}
	if err := setFilter("smudge"); err != nil {
		return err
	}
	if err := requireFilters(); err != nil {
		return err
	}
	return nil
}

func setFilter(filterName string) error {
	key := fmt.Sprintf("filter.lfs.%s", filterName)
	value := fmt.Sprintf("git lfs %s %%f", filterName)

	existing := git.Config.Find(key)
	if shouldReset(existing) {
		git.Config.UnsetGlobal(key)
		git.Config.SetGlobal(key, value)
	} else if existing != value {
		return fmt.Errorf("The %s filter should be \"%s\" but is \"%s\"", filterName, value, existing)
	}

	return nil
}

func requireFilters() error {
	key := "filter.lfs.required"
	value := "true"

	existing := git.Config.Find(key)
	if shouldReset(existing) {
		git.Config.UnsetGlobal(key)
		git.Config.SetGlobal(key, value)
	} else if existing != value {
		return errors.New("Git LFS filters should be required but are not.")
	}

	return nil
}

func shouldReset(value string) bool {
	if len(value) == 0 {
		return true
	}
	return valueRegexp.MatchString(value)
}
<commit_msg>アアー アアアア アー<commit_after>package lfs

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/github/git-lfs/git"
)

var (
	valueRegexp           = regexp.MustCompile("\\Agit[\\-\\s]media")
	NotInARepositoryError = errors.New("Not in a repository")

	prePushHook     = "#!/bin/sh\ncommand -v git-lfs >/dev/null 2>&1 || { echo >&2 \"\\nThis repository has been set up with Git LFS but Git LFS is not installed.\\n\"; exit 0; }\ngit lfs pre-push \"$@\""
	prePushUpgrades = map[string]bool{
		"#!/bin/sh\ngit lfs push --stdin $*":     true,
		"#!/bin/sh\ngit lfs push --stdin \"$@\"": true,
		"#!/bin/sh\ngit lfs pre-push \"$@\"":     true,
	}
)

type HookExists struct {
	Name     string
	Path     string
	Contents string
}

func (e *HookExists) Error() string {
	return fmt.Sprintf("Hook already exists: %s\n\n%s\n", e.Name, e.Contents)
}

func InstallHooks(force bool) error {
	if !InRepo() {
		return NotInARepositoryError
	}

	if err := os.MkdirAll(filepath.Join(LocalGitDir, "hooks"), 0755); err != nil {
		return err
	}

	hookPath := filepath.Join(LocalGitDir, "hooks", "pre-push")
	if _, err := os.Stat(hookPath); err == nil && !force {
		return upgradeHookOrError(hookPath, "pre-push", prePushHook, prePushUpgrades)
	}

	return ioutil.WriteFile(hookPath, []byte(prePushHook+"\n"), 0755)
}

func upgradeHookOrError(hookPath, hookName, hook string, upgrades map[string]bool) error {
	file, err := os.Open(hookPath)
	if err != nil {
		return err
	}

	by, err := ioutil.ReadAll(io.LimitReader(file, 1024))
	file.Close()
	if err != nil {
		return err
	}

	contents := strings.TrimSpace(string(by))
	if contents == hook {
		return nil
	}

	if upgrades[contents] {
		return ioutil.WriteFile(hookPath, []byte(hook+"\n"), 0755)
	}

	return &HookExists{hookName, hookPath, contents}
}

func InstallFilters() error {
	if err := setFilter("clean"); err != nil {
		return err
	}
	if err := setFilter("smudge"); err != nil {
		return err
	}
	if err := requireFilters(); err != nil {
		return err
	}
	return nil
}

func setFilter(filterName string) error {
	key := fmt.Sprintf("filter.lfs.%s", filterName)
	value := fmt.Sprintf("git-lfs %s %%f", filterName)

	existing := git.Config.Find(key)
	if shouldReset(existing) {
		git.Config.UnsetGlobal(key)
		git.Config.SetGlobal(key, value)
	} else if existing != value {
		return fmt.Errorf("The %s filter should be \"%s\" but is \"%s\"", filterName, value, existing)
	}

	return nil
}

func requireFilters() error {
	key := "filter.lfs.required"
	value := "true"

	existing := git.Config.Find(key)
	if shouldReset(existing) {
		git.Config.UnsetGlobal(key)
		git.Config.SetGlobal(key, value)
	} else if existing != value {
		return errors.New("Git LFS filters should be required but are not.")
	}

	return nil
}

func shouldReset(value string) bool {
	if len(value) == 0 {
		return true
	}
	return valueRegexp.MatchString(value)
}
<|endoftext|>


<commit_before>/*
Package workerpool queues work to a limited number of goroutines.

The purpose of the worker pool is to limit the concurrency of the task
performed by the workers.  This is useful when performing a task requires
sufficient resources (CPU, memory, etc.), that running too many tasks at the
same time would exhaust resources.

Non-blocking task submission

A task is a function submitted to the worker pool for execution.  Submitting
tasks to this worker pool will not block, regardless of the number of tasks.
Tasks read from the input task queue are immediately dispatched to an available
worker.  If no worker is immediately available, then the task is passed to a go
routine which is started to wait for an available worker.  This clears the task
from the task queue immediately, whether or not a worker is currently
available, and will not block the submission of tasks.

The intent of the worker pool is to limit the concurrency of task execution,
not limit the number of tasks queued to be executed. Therefore, this unbounded
input of tasks is acceptable as the tasks cannot be discarded.  If the number
of inbound tasks if too many to even queue for pending processing, then this
solution is outside the scope of the worker pool, and should be solved by
distributing load over multiple systems, storing input that requires processing
in some intermediate storage (e.g. a database or file system).

Dispatcher

This worker pool uses a single dispatcher goroutine to read tasks from the
input task queue and dispatch them to a worker goroutine.  This allows for a
small input channel, and lets the dispatcher queue as many tasks as are
submitted when there are no available workers (using goroutines).
Additionally, the dispatcher can adjust the number of workers as appropriate
for the work load, without having to utilize locked counters and checks
incurred on task submission.

Usage note

It is advisable to use different worker pools for tasks that are bound by
different resources, or that have different resource use patterns.  For
example, tasks that use X Mb of memory may need different concurrency limits
than tasks that use Y Mb of memory.

Credits

This implementation builds on ideas from the following:

http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang
http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html

*/
package workerpool

import "time"

const (
	// Size of queue to which tasks are submitted.  This can be small, no
	// matter how many tasks are submitted, because the dispatcher removes
	// tasks from this queue, scheduling each immediately to a ready worker, or
	// to a goroutine that will give it to the next ready worker.
	taskQueueSize = 16

	// If worker pool receives no new work for this period of time, then stop
	// a worker goroutine.
	idleTimeoutSec = 5

	// The minimum number of workers is always zero, because the time to start
	// new workers is insignificant.
	minWorkers = 0
)

type WorkerPool interface {
	// Submit enqueues a function for a worker to execute.
	//
	// Any external values needed by the task function must be captured in a
	// closure.  Any return values should be returned over a channel that is
	// captured in the task function closure.
	//
	// Submit will not block regardless of the number of tasks submitted.  Each
	// task is immediately given to an available worker or passed to a
	// goroutine to be given to the next available worker.  If there are no
	// available workers, the dispatcher adds a worker, until the maximum
	// number of workers is running.
	Submit(task func())

	// Stop stops the worker pool and waits for workers to complete.
	//
	// Since creating the worker pool starts at least one goroutine, for the
	// dispatcher, this function should be called when the worker pool is no
	// longer needed.
	Stop()

	// Stopped returns true if this worker pool has been stopped.
	Stopped() bool
}

// New creates and starts a pool of worker goroutines.
//
// The maxWorkers parameter specifies the maximum number of workers that will
// execute tasks concurrently.  After each timeout period, a worker goroutine
// is stopped until there are no remaining workers.
func New(maxWorkers int) WorkerPool {
	// There must be at least one worker.
	if maxWorkers < 1 {
		maxWorkers = 1
	}

	pool := &workerPool{
		taskQueue:    make(chan func(), taskQueueSize),
		maxWorkers:   maxWorkers,
		readyWorkers: make(chan chan func(), maxWorkers),
		timeout:      time.Second * idleTimeoutSec,
		stoppedChan:  make(chan struct{}),
	}

	// Start the task dispatcher.
	go pool.dispatch()

	return pool
}

type workerPool struct {
	maxWorkers   int
	timeout      time.Duration
	taskQueue    chan func()
	readyWorkers chan chan func()
	stoppedChan  chan struct{}
}

// Stop stops the worker pool and waits for workers to complete.
func (p *workerPool) Stop() {
	if p.Stopped() {
		return
	}
	close(p.taskQueue)
	<-p.stoppedChan
}

// Stopped returns true if this worker pool has been stopped.
func (p *workerPool) Stopped() bool {
	select {
	case <-p.stoppedChan:
		return true
	default:
	}
	return false
}

// Submit enqueues a function for a worker to execute.
func (p *workerPool) Submit(task func()) {
	if task != nil {
		p.taskQueue <- task
	}
}

// dispatch sends the next queued task to an available worker.
func (p *workerPool) dispatch() {
	defer close(p.stoppedChan)
	timeout := time.NewTimer(p.timeout)
	var workerCount int
	var task func()
	var ok bool
	var workerTaskChan chan func()
shutdown:
	for {
		timeout.Reset(p.timeout)
		select {
		case task, ok = <-p.taskQueue:
			if !ok {
				break shutdown
			}
			// Got a task to do.
			select {
			case workerTaskChan = <-p.readyWorkers:
				// A worker is ready, so give task to worker.
				workerTaskChan <- task
			default:
				// No workers ready.
				// Create a new worker, if not at max.
				if workerCount < p.maxWorkers {
					workerCount++
					startWorker(p.readyWorkers)
				}
				// Start a goroutine to submit the task when a worker is ready.
				go func(t func()) {
					taskChan := <-p.readyWorkers
					taskChan <- t
				}(task)
			}
		case <-timeout.C:
			// Timed out waiting for work to arrive.  Kill a ready worker.
			if workerCount > minWorkers {
				select {
				case workerTaskChan = <-p.readyWorkers:
					// A worker is ready, so kill.
					close(workerTaskChan)
					workerCount--
				default:
					// No work, but no ready workers.  All workers are busy.
				}
			}
		}
	}

	// Stop all remaining workers as they become ready.
	for workerCount > 0 {
		workerTaskChan = <-p.readyWorkers
		close(workerTaskChan)
		workerCount--
	}
}

// startWorker starts a goroutine that executes tasks given by the dispatcher.
//
// A worker registers that is it available to do work by putting its task
// channel on the readyWorkers channel.  The dispatcher reads a worker's task
// channel from the readyWorkers channel, and writes a task to the worker over
// the worker's task channel.  To stop a worker, the dispatcher closes a
// worker's task channel, instead of writing a task to it.
func startWorker(readyWorkers chan chan func()) {
	go func() {
		taskChan := make(chan func())
		var task func()
		var ok bool
		for {
			// Register availability on readyWorkers channel.
			readyWorkers <- taskChan

			// Read task from dispatcher.
			task, ok = <-taskChan
			if !ok {
				// Dispatcher has told worker to stop.
				break
			}

			// Execute the task.
			task()
		}
	}()
}
<commit_msg>min workers always zero<commit_after>/*
Package workerpool queues work to a limited number of goroutines.

The purpose of the worker pool is to limit the concurrency of the task
performed by the workers.  This is useful when performing a task requires
sufficient resources (CPU, memory, etc.), that running too many tasks at the
same time would exhaust resources.

Non-blocking task submission

A task is a function submitted to the worker pool for execution.  Submitting
tasks to this worker pool will not block, regardless of the number of tasks.
Tasks read from the input task queue are immediately dispatched to an available
worker.  If no worker is immediately available, then the task is passed to a go
routine which is started to wait for an available worker.  This clears the task
from the task queue immediately, whether or not a worker is currently
available, and will not block the submission of tasks.

The intent of the worker pool is to limit the concurrency of task execution,
not limit the number of tasks queued to be executed. Therefore, this unbounded
input of tasks is acceptable as the tasks cannot be discarded.  If the number
of inbound tasks if too many to even queue for pending processing, then this
solution is outside the scope of the worker pool, and should be solved by
distributing load over multiple systems, storing input that requires processing
in some intermediate storage (e.g. a database or file system).

Dispatcher

This worker pool uses a single dispatcher goroutine to read tasks from the
input task queue and dispatch them to a worker goroutine.  This allows for a
small input channel, and lets the dispatcher queue as many tasks as are
submitted when there are no available workers (using goroutines).
Additionally, the dispatcher can adjust the number of workers as appropriate
for the work load, without having to utilize locked counters and checks
incurred on task submission.

When no tasks have been submitted for a period of time, a worker is removed by
the dispatcher.  This is done until there are no more workers to remove.  The
minimum number of workers is always zero, because the time to start new workers
is insignificant.

Usage note

It is advisable to use different worker pools for tasks that are bound by
different resources, or that have different resource use patterns.  For
example, tasks that use X Mb of memory may need different concurrency limits
than tasks that use Y Mb of memory.

Credits

This implementation builds on ideas from the following:

http://marcio.io/2015/07/handling-1-million-requests-per-minute-with-golang
http://nesv.github.io/golang/2014/02/25/worker-queues-in-go.html

*/
package workerpool

import "time"

const (
	// Size of queue to which tasks are submitted.  This can be small, no
	// matter how many tasks are submitted, because the dispatcher removes
	// tasks from this queue, scheduling each immediately to a ready worker, or
	// to a goroutine that will give it to the next ready worker.
	taskQueueSize = 16

	// If worker pool receives no new work for this period of time, then stop
	// a worker goroutine.
	idleTimeoutSec = 5
)

type WorkerPool interface {
	// Submit enqueues a function for a worker to execute.
	//
	// Any external values needed by the task function must be captured in a
	// closure.  Any return values should be returned over a channel that is
	// captured in the task function closure.
	//
	// Submit will not block regardless of the number of tasks submitted.  Each
	// task is immediately given to an available worker or passed to a
	// goroutine to be given to the next available worker.  If there are no
	// available workers, the dispatcher adds a worker, until the maximum
	// number of workers is running.
	Submit(task func())

	// Stop stops the worker pool and waits for workers to complete.
	//
	// Since creating the worker pool starts at least one goroutine, for the
	// dispatcher, this function should be called when the worker pool is no
	// longer needed.
	Stop()

	// Stopped returns true if this worker pool has been stopped.
	Stopped() bool
}

// New creates and starts a pool of worker goroutines.
//
// The maxWorkers parameter specifies the maximum number of workers that will
// execute tasks concurrently.  After each timeout period, a worker goroutine
// is stopped until there are no remaining workers.
func New(maxWorkers int) WorkerPool {
	// There must be at least one worker.
	if maxWorkers < 1 {
		maxWorkers = 1
	}

	pool := &workerPool{
		taskQueue:    make(chan func(), taskQueueSize),
		maxWorkers:   maxWorkers,
		readyWorkers: make(chan chan func(), maxWorkers),
		timeout:      time.Second * idleTimeoutSec,
		stoppedChan:  make(chan struct{}),
	}

	// Start the task dispatcher.
	go pool.dispatch()

	return pool
}

type workerPool struct {
	maxWorkers   int
	timeout      time.Duration
	taskQueue    chan func()
	readyWorkers chan chan func()
	stoppedChan  chan struct{}
}

// Stop stops the worker pool and waits for workers to complete.
func (p *workerPool) Stop() {
	if p.Stopped() {
		return
	}
	close(p.taskQueue)
	<-p.stoppedChan
}

// Stopped returns true if this worker pool has been stopped.
func (p *workerPool) Stopped() bool {
	select {
	case <-p.stoppedChan:
		return true
	default:
	}
	return false
}

// Submit enqueues a function for a worker to execute.
func (p *workerPool) Submit(task func()) {
	if task != nil {
		p.taskQueue <- task
	}
}

// dispatch sends the next queued task to an available worker.
func (p *workerPool) dispatch() {
	defer close(p.stoppedChan)
	timeout := time.NewTimer(p.timeout)
	var workerCount int
	var task func()
	var ok bool
	var workerTaskChan chan func()
shutdown:
	for {
		timeout.Reset(p.timeout)
		select {
		case task, ok = <-p.taskQueue:
			if !ok {
				break shutdown
			}
			// Got a task to do.
			select {
			case workerTaskChan = <-p.readyWorkers:
				// A worker is ready, so give task to worker.
				workerTaskChan <- task
			default:
				// No workers ready.
				// Create a new worker, if not at max.
				if workerCount < p.maxWorkers {
					workerCount++
					startWorker(p.readyWorkers)
				}
				// Start a goroutine to submit the task when a worker is ready.
				go func(t func()) {
					taskChan := <-p.readyWorkers
					taskChan <- t
				}(task)
			}
		case <-timeout.C:
			// Timed out waiting for work to arrive.  Kill a ready worker.
			if workerCount > 0 {
				select {
				case workerTaskChan = <-p.readyWorkers:
					// A worker is ready, so kill.
					close(workerTaskChan)
					workerCount--
				default:
					// No work, but no ready workers.  All workers are busy.
				}
			}
		}
	}

	// Stop all remaining workers as they become ready.
	for workerCount > 0 {
		workerTaskChan = <-p.readyWorkers
		close(workerTaskChan)
		workerCount--
	}
}

// startWorker starts a goroutine that executes tasks given by the dispatcher.
//
// A worker registers that is it available to do work by putting its task
// channel on the readyWorkers channel.  The dispatcher reads a worker's task
// channel from the readyWorkers channel, and writes a task to the worker over
// the worker's task channel.  To stop a worker, the dispatcher closes a
// worker's task channel, instead of writing a task to it.
func startWorker(readyWorkers chan chan func()) {
	go func() {
		taskChan := make(chan func())
		var task func()
		var ok bool
		for {
			// Register availability on readyWorkers channel.
			readyWorkers <- taskChan

			// Read task from dispatcher.
			task, ok = <-taskChan
			if !ok {
				// Dispatcher has told worker to stop.
				break
			}

			// Execute the task.
			task()
		}
	}()
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/syndtr/goleveldb/leveldb"
	de "github.com/syndtr/goleveldb/leveldb/errors"
)

func OpenLocalDB() (*leveldb.DB, error) {
	dbDir := getDictDBDir()
	db, err := leveldb.OpenFile(dbDir, nil)
	if nil != err {
		return nil, err
	}
	return db, nil
}

func QueryLocalDB(key string, db *leveldb.DB) (*DictResult, error) {
	data, err := db.Get([]byte(key), nil)
	if de.ErrNotFound == err {
		// first query word always return NotFound
		return nil, nil
	}
	if nil != err {
		return nil, err
	}

	ret := DictResult{}
	if err := json.Unmarshal(data, &ret); nil != err {
		return nil, err
	}
	return &ret, nil
}

func ScanWords() (map[string]DictResult, error) {
	db, err := OpenLocalDB()
	if err != nil {
		color.Red("OpenLocalDb Fail! Cause: %s", err)
		return nil, err
	}

	defer db.Close()

	dict := map[string]DictResult{}
	iter := db.NewIterator(nil, nil)
	for iter.Next() {
		ret := DictResult{}
		if err := json.Unmarshal(iter.Value(), &ret); nil != err {
			return nil, err
		}

		dict[string(iter.Key())] = ret
	}

	defer iter.Release()
	return dict, nil
}

func DeleteWords(args []string) error {
	db, err := OpenLocalDB()
	if err != nil {
		color.Red("OpenLocalDb Fail! Cause: %s", err)
		return err
	}
	defer db.Close()

	if err := db.Delete([]byte(strings.Join(args, " ")), nil); err != nil {
		return err
	}

	return nil
}

func BackupCahceFiles() {
	dictDir := getDictDir()
	if _, err := os.Stat(dictDir); err != nil {
		color.Red("Cannot find the DB path", err.Error())
		return
	}

	fileName := GetBakFileName()
	dstFile := filepath.Join(dictDir, fileName)

	result := Execute(dictDir, "tar", "-czvf", dstFile, "./db")
	if result {
		color.Green("Local DB backup to: %s", dstFile)
	}
}

func ClearCahceFiles() {
	tmpDir := getDictDir()
	err := os.RemoveAll(tmpDir)
	if nil != err && !os.IsNotExist(err) {
		color.Red("ClearCacheFile Fail! Cause: %s", err.Error())
	}
	color.Green("Clear Success! CacheDir: %s", tmpDir)
}

func getDictDir() string {
	dbDir := os.Getenv("YDICT_DB")
	if dbDir == "" {
		dbDir = filepath.Join(os.Getenv("HOME"), ".ydict")
	}

	return dbDir
}

func getDictDBDir() string {
	dbDir := os.Getenv("YDICT_DB")
	if dbDir == "" {
		dbDir = filepath.Join(os.Getenv("HOME"), ".ydict")
	}

	ydictDir := filepath.Join(dbDir, "db")
	return ydictDir
}

func getDictAudioDir() string {
	tmpDir := getDictDir()
	ydictDir := filepath.Join(tmpDir, "audio")
	return ydictDir
}

func SaveVoiceFile(name string, body io.ReadCloser) (string, error) {
	ydictDir := getDictAudioDir()
	tmpfile, err := ioutil.TempFile(ydictDir, name)
	if err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
		err = os.MkdirAll(ydictDir, 0700)
		if nil != err {
			return "", err
		}
		tmpfile, err = ioutil.TempFile(ydictDir, name)
		if nil != err {
			return "", err
		}
	}

	data, err := ioutil.ReadAll(body)
	if err != nil {
		return "", err
	}

	if _, err := tmpfile.Write(data); err != nil {
		return "", err
	}

	if err := tmpfile.Close(); err != nil {
		return "", err
	}

	aFile := tmpfile.Name()
	return aFile, err

}

func DoPlayFile(aFile string) error {
	cmd := exec.Command("mpg123", aFile)
	if _, err := exec.LookPath("mpv"); err == nil {
		// andoird termux only have mpv
		cmd = exec.Command("mpv", aFile)
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		return err
	}
	return nil
}
<commit_msg>refactor check DB path function<commit_after>package lib

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/fatih/color"
	"github.com/syndtr/goleveldb/leveldb"
	de "github.com/syndtr/goleveldb/leveldb/errors"
)

func OpenLocalDB() (*leveldb.DB, error) {
	dbDir := getDictDBDir()
	db, err := leveldb.OpenFile(dbDir, nil)
	if nil != err {
		return nil, err
	}
	return db, nil
}

func QueryLocalDB(key string, db *leveldb.DB) (*DictResult, error) {
	data, err := db.Get([]byte(key), nil)
	if de.ErrNotFound == err {
		// first query word always return NotFound
		return nil, nil
	}
	if nil != err {
		return nil, err
	}

	ret := DictResult{}
	if err := json.Unmarshal(data, &ret); nil != err {
		return nil, err
	}
	return &ret, nil
}

func ScanWords() (map[string]DictResult, error) {
	db, err := OpenLocalDB()
	if err != nil {
		color.Red("OpenLocalDb Fail! Cause: %s", err)
		return nil, err
	}

	defer db.Close()

	dict := map[string]DictResult{}
	iter := db.NewIterator(nil, nil)
	for iter.Next() {
		ret := DictResult{}
		if err := json.Unmarshal(iter.Value(), &ret); nil != err {
			return nil, err
		}

		dict[string(iter.Key())] = ret
	}

	defer iter.Release()
	return dict, nil
}

func DeleteWords(args []string) error {
	db, err := OpenLocalDB()
	if err != nil {
		color.Red("OpenLocalDb Fail! Cause: %s", err)
		return err
	}
	defer db.Close()

	if err := db.Delete([]byte(strings.Join(args, " ")), nil); err != nil {
		return err
	}

	return nil
}

func BackupCahceFiles() {
	dictDir := getDictDir()
	if _, err := os.Stat(dictDir); os.IsNotExist(err) {
		color.Red("Cannot find the DB path", err.Error())
		return
	}

	fileName := GetBakFileName()
	dstFile := filepath.Join(dictDir, fileName)

	result := Execute(dictDir, "tar", "-czvf", dstFile, "./db")
	if result {
		color.Green("Local DB backup to: %s", dstFile)
	}
}

func ClearCahceFiles() {
	tmpDir := getDictDir()
	err := os.RemoveAll(tmpDir)
	if nil != err && !os.IsNotExist(err) {
		color.Red("ClearCacheFile Fail! Cause: %s", err.Error())
	}
	color.Green("Clear Success! CacheDir: %s", tmpDir)
}

func getDictDir() string {
	dbDir := os.Getenv("YDICT_DB")
	if dbDir == "" {
		dbDir = filepath.Join(os.Getenv("HOME"), ".ydict")
	}

	return dbDir
}

func getDictDBDir() string {
	dbDir := os.Getenv("YDICT_DB")
	if dbDir == "" {
		dbDir = filepath.Join(os.Getenv("HOME"), ".ydict")
	}

	ydictDir := filepath.Join(dbDir, "db")
	return ydictDir
}

func getDictAudioDir() string {
	tmpDir := getDictDir()
	ydictDir := filepath.Join(tmpDir, "audio")
	return ydictDir
}

func SaveVoiceFile(name string, body io.ReadCloser) (string, error) {
	ydictDir := getDictAudioDir()
	tmpfile, err := ioutil.TempFile(ydictDir, name)
	if err != nil {
		if !os.IsNotExist(err) {
			return "", err
		}
		err = os.MkdirAll(ydictDir, 0700)
		if nil != err {
			return "", err
		}
		tmpfile, err = ioutil.TempFile(ydictDir, name)
		if nil != err {
			return "", err
		}
	}

	data, err := ioutil.ReadAll(body)
	if err != nil {
		return "", err
	}

	if _, err := tmpfile.Write(data); err != nil {
		return "", err
	}

	if err := tmpfile.Close(); err != nil {
		return "", err
	}

	aFile := tmpfile.Name()
	return aFile, err

}

func DoPlayFile(aFile string) error {
	cmd := exec.Command("mpg123", aFile)
	if _, err := exec.LookPath("mpv"); err == nil {
		// andoird termux only have mpv
		cmd = exec.Command("mpv", aFile)
	}

	if err := cmd.Start(); err != nil {
		return err
	}

	if err := cmd.Wait(); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package options

import (
	"time"

	"github.com/spf13/pflag"
)

type KubeRouterConfig struct {
	Kubeconfig         string
	Master             string
	ConfigSyncPeriod   time.Duration
	CleanupConfig      bool
	IPTablesSyncPeriod time.Duration
	IpvsSyncPeriod     time.Duration
	RoutesSyncPeriod   time.Duration
	RunServiceProxy    bool
	RunFirewall        bool
	RunRouter          bool
	MasqueradeAll      bool
}

func NewKubeRouterConfig() *KubeRouterConfig {
	return &KubeRouterConfig{ConfigSyncPeriod: 1 * time.Minute,
		IpvsSyncPeriod:     1 * time.Minute,
		IPTablesSyncPeriod: 1 * time.Minute,
		RoutesSyncPeriod:   1 * time.Minute,
		MasqueradeAll:      false,
		RunServiceProxy:    true,
		RunFirewall:        true,
		RunRouter:          false}
}

func (s *KubeRouterConfig) AddFlags(fs *pflag.FlagSet) {
	fs.BoolVar(&s.RunServiceProxy, "run-service-proxy", s.RunServiceProxy, "If false, kube-router wont setup IPVS for services proxy. True by default.")
	fs.BoolVar(&s.RunFirewall, "run-firewall", s.RunFirewall, "If false, kube-router wont setup iptables to provide ingress firewall for pods. True by default.")
	fs.BoolVar(&s.RunRouter, "run-router", s.RunRouter, "If true each node advertise routes the rest of the nodes and learn the routes for the pods. True by default.")
	fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
	fs.StringVar(&s.Kubeconfig, "kubeconfig", s.Kubeconfig, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
	fs.BoolVar(&s.CleanupConfig, "cleanup-config", s.CleanupConfig, "If true cleanup iptables rules, ipvs, ipset configuration and exit.")
	fs.BoolVar(&s.MasqueradeAll, "masquerade-all", s.MasqueradeAll, "SNAT all traffic to cluster IP/node port. False by default")
	fs.DurationVar(&s.ConfigSyncPeriod, "config-sync-period", s.ConfigSyncPeriod, "How often configuration from the apiserver is refreshed.  Must be greater than 0.")
	fs.DurationVar(&s.IPTablesSyncPeriod, "iptables-sync-period", s.IPTablesSyncPeriod, "The maximum interval of how often iptables rules are refreshed (e.g. '5s', '1m'). Must be greater than 0.")
	fs.DurationVar(&s.IpvsSyncPeriod, "ipvs-sync-period", s.IpvsSyncPeriod, "The maximum interval of how often ipvs config is refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0.")
	fs.DurationVar(&s.RoutesSyncPeriod, "routes-sync-period", s.RoutesSyncPeriod, "The maximum interval of how often routes are adrvertised and learned (e.g. '5s', '1m', '2h22m'). Must be greater than 0.")
}
<commit_msg>set --run-router flag to true by default<commit_after>package options

import (
	"time"

	"github.com/spf13/pflag"
)

type KubeRouterConfig struct {
	Kubeconfig         string
	Master             string
	ConfigSyncPeriod   time.Duration
	CleanupConfig      bool
	IPTablesSyncPeriod time.Duration
	IpvsSyncPeriod     time.Duration
	RoutesSyncPeriod   time.Duration
	RunServiceProxy    bool
	RunFirewall        bool
	RunRouter          bool
	MasqueradeAll      bool
}

func NewKubeRouterConfig() *KubeRouterConfig {
	return &KubeRouterConfig{ConfigSyncPeriod: 1 * time.Minute,
		IpvsSyncPeriod:     1 * time.Minute,
		IPTablesSyncPeriod: 1 * time.Minute,
		RoutesSyncPeriod:   1 * time.Minute,
		MasqueradeAll:      false,
		RunServiceProxy:    true,
		RunFirewall:        true,
		RunRouter:          true}
}

func (s *KubeRouterConfig) AddFlags(fs *pflag.FlagSet) {
	fs.BoolVar(&s.RunServiceProxy, "run-service-proxy", s.RunServiceProxy, "If false, kube-router wont setup IPVS for services proxy. True by default.")
	fs.BoolVar(&s.RunFirewall, "run-firewall", s.RunFirewall, "If false, kube-router wont setup iptables to provide ingress firewall for pods. True by default.")
	fs.BoolVar(&s.RunRouter, "run-router", s.RunRouter, "If true each node advertise routes the rest of the nodes and learn the routes for the pods. True by default.")
	fs.StringVar(&s.Master, "master", s.Master, "The address of the Kubernetes API server (overrides any value in kubeconfig)")
	fs.StringVar(&s.Kubeconfig, "kubeconfig", s.Kubeconfig, "Path to kubeconfig file with authorization information (the master location is set by the master flag).")
	fs.BoolVar(&s.CleanupConfig, "cleanup-config", s.CleanupConfig, "If true cleanup iptables rules, ipvs, ipset configuration and exit.")
	fs.BoolVar(&s.MasqueradeAll, "masquerade-all", s.MasqueradeAll, "SNAT all traffic to cluster IP/node port. False by default")
	fs.DurationVar(&s.ConfigSyncPeriod, "config-sync-period", s.ConfigSyncPeriod, "How often configuration from the apiserver is refreshed.  Must be greater than 0.")
	fs.DurationVar(&s.IPTablesSyncPeriod, "iptables-sync-period", s.IPTablesSyncPeriod, "The maximum interval of how often iptables rules are refreshed (e.g. '5s', '1m'). Must be greater than 0.")
	fs.DurationVar(&s.IpvsSyncPeriod, "ipvs-sync-period", s.IpvsSyncPeriod, "The maximum interval of how often ipvs config is refreshed (e.g. '5s', '1m', '2h22m'). Must be greater than 0.")
	fs.DurationVar(&s.RoutesSyncPeriod, "routes-sync-period", s.RoutesSyncPeriod, "The maximum interval of how often routes are adrvertised and learned (e.g. '5s', '1m', '2h22m'). Must be greater than 0.")
}
<|endoftext|>


<commit_before>package utils

import (
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
	"time"

	"github.com/UniversityRadioYork/2016-site/structs"
	myradio "github.com/UniversityRadioYork/myradio-go"
	"github.com/gedex/inflector"
)

// TemplatePrefix is the constant containing the filepath prefix for templates.
const TemplatePrefix = "views"

// BaseTemplates is the array of 'base' templates used in each template render.
var BaseTemplates = []string{
	"partials/header.tmpl",
	"partials/footer.tmpl",
	"elements/navbar.tmpl",
	"partials/base.tmpl",
}

// RenderTemplate renders a 2016site template on the ResponseWriter w.
//
// This function automatically adds in the 2016site base templates, performs
// error handling, and builds a global context.
//
// The PageContext context gives the context for the page to be rendered, sent
// to the template as PageContext.
// The interface{} data gives the data to be sent to the template as PageData.
//
// The string mainTmpl gives the name, relative to views, of the main
// template to render.  The variadic argument addTmpls names any additional
// templates mainTmpl depends on.
//
// RenderTemplate returns any error that occurred when rendering the template.
func RenderTemplate(w http.ResponseWriter, context structs.PageContext, data interface{}, mainTmpl string, addTmpls ...string) error {
	var err error

	td := structs.Globals{
		PageContext: context,
		PageData:    data,
	}

	ownTmpls := append(addTmpls, mainTmpl)
	baseTmpls := append(BaseTemplates, ownTmpls...)

	var tmpls []string
	for _, baseTmpl := range baseTmpls {
		tmpls = append(tmpls, filepath.Join(TemplatePrefix, baseTmpl))
	}

	t := template.New("base.tmpl")
	t.Funcs(template.FuncMap{
		"url":  func(s string) string { return PrefixURL(s, context.URLPrefix) },
		"html": renderHTML,
		//Takes a splice of show meta and returns the last x elements
		"getLastShowMeta": func(a []myradio.ShowMeta, amount int) []myradio.ShowMeta {
			if len(a) < amount {
				return a
			}
			return a[len(a)-amount:]

		},
		//Takes a splice of seasons and returns the total number of episodes
		"showCount": func(seasons []myradio.Season) int {
			var c = 0
			for _, season := range seasons {
				//Something about JSON being read as a float 64 so needing to convert to an int
				c += int(season.NumEpisodes.Value.(float64))
			}
			return c
		},
		"showsToHours": func(shows []myradio.ShowMeta) int {
			//TODO: finish This
			return -5
		},
		"formatDuration": func(d time.Duration) string {
			s := d.String()
			var output = ""
			var startIndexLastNumerical = 0
			var suffix = ""
			for index := range s {
				if s[index] == []byte("d")[0] {
					suffix = " Day"
				} else if s[index] == []byte("h")[0] {
					suffix = " Hour"
				} else if s[index] == []byte("m")[0] {
					suffix = " Min"
				} else {
					suffix = ""
				}
				if suffix != "" {
					var value = ""
					var visible = true
					var plural = true
					value = string(s[startIndexLastNumerical:index])
					if len(value) == 1 {
						if value == "0" {
							visible = false
						} else if value == "1" {
							plural = false
						}
					}
					if visible {
						output = output + value + suffix
						if plural {
							output = output + "s"
						}
					}
					if index < len(s)-1 {
						startIndexLastNumerical = index + 1
					}
				}
			}
			return output
		},
		// TODO(CaptainHayashi): this is temporary
		"stripHTML": func(s string) string {
			d, err := StripHTML(s)
			if err != nil {
				return "Error stripping HTML"
			}
			return d
		},
		"week":   FormatWeekRelative,
		"plural": inflector.Pluralize,
	})
	t, err = t.ParseFiles(tmpls...)
	if err != nil {
		return err
	}

	return t.Execute(w, td)
}

// renderHTML takes some html as a string and returns a template.HTML
//
// Handles plain text gracefully.
func renderHTML(value interface{}) template.HTML {
	return template.HTML(fmt.Sprint(value))
}
<commit_msg>Simplify formatDuration further<commit_after>package utils

import (
	"fmt"
	"html/template"
	"net/http"
	"path/filepath"
	"strings"
	"time"

	"github.com/UniversityRadioYork/2016-site/structs"
	myradio "github.com/UniversityRadioYork/myradio-go"
	"github.com/gedex/inflector"
)

// TemplatePrefix is the constant containing the filepath prefix for templates.
const TemplatePrefix = "views"

// BaseTemplates is the array of 'base' templates used in each template render.
var BaseTemplates = []string{
	"partials/header.tmpl",
	"partials/footer.tmpl",
	"elements/navbar.tmpl",
	"partials/base.tmpl",
}

// RenderTemplate renders a 2016site template on the ResponseWriter w.
//
// This function automatically adds in the 2016site base templates, performs
// error handling, and builds a global context.
//
// The PageContext context gives the context for the page to be rendered, sent
// to the template as PageContext.
// The interface{} data gives the data to be sent to the template as PageData.
//
// The string mainTmpl gives the name, relative to views, of the main
// template to render.  The variadic argument addTmpls names any additional
// templates mainTmpl depends on.
//
// RenderTemplate returns any error that occurred when rendering the template.
func RenderTemplate(w http.ResponseWriter, context structs.PageContext, data interface{}, mainTmpl string, addTmpls ...string) error {
	var err error

	td := structs.Globals{
		PageContext: context,
		PageData:    data,
	}

	ownTmpls := append(addTmpls, mainTmpl)
	baseTmpls := append(BaseTemplates, ownTmpls...)

	var tmpls []string
	for _, baseTmpl := range baseTmpls {
		tmpls = append(tmpls, filepath.Join(TemplatePrefix, baseTmpl))
	}

	t := template.New("base.tmpl")
	t.Funcs(template.FuncMap{
		"url":  func(s string) string { return PrefixURL(s, context.URLPrefix) },
		"html": renderHTML,
		//Takes a splice of show meta and returns the last x elements
		"getLastShowMeta": func(a []myradio.ShowMeta, amount int) []myradio.ShowMeta {
			if len(a) < amount {
				return a
			}
			return a[len(a)-amount:]

		},
		//Takes a splice of seasons and returns the total number of episodes
		"showCount": func(seasons []myradio.Season) int {
			var c = 0
			for _, season := range seasons {
				//Something about JSON being read as a float 64 so needing to convert to an int
				c += int(season.NumEpisodes.Value.(float64))
			}
			return c
		},
		"showsToHours": func(shows []myradio.ShowMeta) int {
			//TODO: finish This
			return -5
		},
		"formatDuration": func(d time.Duration) string {
			days := int64(d.Hours()) / 24
			hours := int64(d.Hours()) % 24
			minutes := int64(d.Minutes()) % 60
			seconds := int64(d.Seconds()) % 60

			segments := []struct {
				name  string
				value int64
			}{
				{"Day", days},
				{"Hour", hours},
				{"Min", minutes},
				{"Sec", seconds},
			}

			parts := []string{}

			for _, s := range segments {
				if s.value == 0 {
					continue
				}
				plural := ""
				if s.value == 1 {
					plural = "s"
				}

				parts = append(parts, fmt.Sprintf("%d %s%s", s.value, s.name, plural))
			}
			return strings.Join(parts, " ")
		},
		// TODO(CaptainHayashi): this is temporary
		"stripHTML": func(s string) string {
			d, err := StripHTML(s)
			if err != nil {
				return "Error stripping HTML"
			}
			return d
		},
		"week":   FormatWeekRelative,
		"plural": inflector.Pluralize,
	})
	t, err = t.ParseFiles(tmpls...)
	if err != nil {
		return err
	}

	return t.Execute(w, td)
}

// renderHTML takes some html as a string and returns a template.HTML
//
// Handles plain text gracefully.
func renderHTML(value interface{}) template.HTML {
	return template.HTML(fmt.Sprint(value))
}
<|endoftext|>


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

package db_service

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/GoogleCloudPlatform/gcp-service-broker/brokerapi/brokers/models"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/broker"
	"github.com/GoogleCloudPlatform/gcp-service-broker/utils"
	"github.com/jinzhu/gorm"
	googlecloudsql "google.golang.org/api/sqladmin/v1beta4"
)

// runs schema migrations on the provided service broker database to get it up to date
func RunMigrations(db *gorm.DB) error {

	migrations := make([]func() error, 3)

	// initial migration - creates tables
	migrations[0] = func() error {
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS service_instance_details (
			  id varchar(255) NOT NULL DEFAULT '',
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  name varchar(255) DEFAULT NULL,
			  location varchar(255) DEFAULT NULL,
			  url varchar(255) DEFAULT NULL,
			  other_details text,
			  service_id varchar(255) DEFAULT NULL,
			  plan_id varchar(255) DEFAULT NULL,
			  space_guid varchar(255) DEFAULT NULL,
			  organization_guid varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS service_binding_credentials (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  other_details text,
			  service_id varchar(255) DEFAULT NULL,
			  service_instance_id varchar(255) DEFAULT NULL,
			  binding_id varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id),
			  KEY idx_service_binding_credentials_deleted_at (deleted_at)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS provision_request_details (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  service_instance_id varchar(255) DEFAULT NULL,
			  request_details varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id),
			  KEY idx_provision_request_details_deleted_at (deleted_at)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS plan_details (
			  id varchar(255) NOT NULL DEFAULT '',
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  service_id varchar(255) DEFAULT NULL,
			  name varchar(255) DEFAULT NULL,
			  features text,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  migration_id int(10) DEFAULT NULL,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		return nil
	}

	// adds CloudOperation table
	migrations[1] = func() error {
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS cloud_operations (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  name varchar(255) DEFAULT NULL,
			  status varchar(255) DEFAULT NULL,
			  operation_type varchar(255) DEFAULT NULL,
			  error_message text,
			  insert_time varchar(255) DEFAULT NULL,
			  start_time varchar(255) DEFAULT NULL,
			  target_id varchar(255) DEFAULT NULL,
			  target_link varchar(255) DEFAULT NULL,
			  service_id varchar(255) DEFAULT NULL,
			  service_instance_id varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}

		// copy provision request details into service instance details
		serviceAccount := make(map[string]string)
		if err := json.Unmarshal([]byte(models.GetServiceAccountJson()), &serviceAccount); err != nil {
			return fmt.Errorf("Could not unmarshal service account details. %v", err)
		}

		cfg, err := utils.GetAuthedConfig()
		if err != nil {
			return fmt.Errorf("Error getting authorized http client: %s", err)
		}

		var prs []models.ProvisionRequestDetails
		if err := DbConnection.Find(&prs).Error; err != nil {
			return err
		}

		for _, pr := range prs {
			var si models.ServiceInstanceDetails
			if err := DbConnection.Where("id = ?", pr.ServiceInstanceId).First(&si).Error; err != nil {
				return err
			}
			od := make(map[string]string)
			if err := json.Unmarshal([]byte(pr.RequestDetails), &od); err != nil {
				return err
			}
			newOd := make(map[string]string)

			// cloudsql
			svc, err := broker.GetServiceById(si.ServiceId)
			if err != nil {
				return err
			}

			switch svc.Name {
			case models.CloudsqlMySQLName:
				newOd["instance_name"] = od["instance_name"]
				newOd["database_name"] = od["database_name"]

				sqlService, err := googlecloudsql.New(cfg.Client(context.Background()))
				if err != nil {
					return fmt.Errorf("Error creating new CloudSQL Client: %s", err)
				}
				dbService := googlecloudsql.NewInstancesService(sqlService)
				clouddb, err := dbService.Get(serviceAccount["project_id"], od["instance_name"]).Do()
				if err != nil {
					return fmt.Errorf("Error getting instance from api: %s", err)
				}
				newOd["host"] = clouddb.IpAddresses[0].IpAddress

			// bigquery
			case models.BigqueryName:
				newOd["dataset_id"] = od["name"]
			// ml apis
			case models.MlName:
				// n/a
			// storage
			case models.StorageName:
				newOd["bucket_name"] = od["name"]

			// pubsub
			case models.PubsubName:
				newOd["topic_name"] = od["topic_name"]
				newOd["subscription_name"] = od["subscription_name"]
			default:
				return fmt.Errorf("unrecognized service: %s", si.ServiceId)
			}

			odBytes, err := json.Marshal(&newOd)
			if err != nil {
				return err
			}
			si.OtherDetails = string(odBytes)
			if err := DbConnection.Save(&si).Error; err != nil {
				return err
			}
		}

		return nil
	}

	// drops plan details table
	migrations[2] = func() error {
		// NOOP migration, this was used to drop the plan_details table, but
		// there's more of a disincentive than incentive to do that because it could
		// leave operators wiping out plain details accidentally and not being able
		// to recover if they don't follow the upgrade path.
		return nil
	}

	var lastMigrationNumber = -1

	// if we've run any migrations before, we should have a migrations table, so find the last one we ran
	if db.HasTable("migrations") {
		var storedMigrations []models.Migration
		if err := db.Order("migration_id desc").Find(&storedMigrations).Error; err != nil {
			return fmt.Errorf("Error getting last migration id even though migration table exists: %s", err)
		}
		lastMigrationNumber = storedMigrations[0].MigrationId
	}

	// starting from the last migration we ran + 1, run migrations until we are current
	for i := lastMigrationNumber + 1; i < len(migrations); i++ {
		tx := db.Begin()
		err := migrations[i]()
		if err != nil {
			tx.Rollback()

			return err
		} else {
			newMigration := models.Migration{
				MigrationId: i,
			}
			if err := db.Save(&newMigration).Error; err != nil {
				tx.Rollback()
				return err
			} else {
				tx.Commit()
			}
		}
	}
	return nil
}
<commit_msg>use correct db instance and try instantiating provisionrequestdetails<commit_after>// Copyright the Service Broker Project Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////
//

package db_service

import (
	"context"
	"encoding/json"
	"fmt"

	"github.com/GoogleCloudPlatform/gcp-service-broker/brokerapi/brokers/models"
	"github.com/GoogleCloudPlatform/gcp-service-broker/pkg/broker"
	"github.com/GoogleCloudPlatform/gcp-service-broker/utils"
	"github.com/jinzhu/gorm"
	googlecloudsql "google.golang.org/api/sqladmin/v1beta4"
)

// runs schema migrations on the provided service broker database to get it up to date
func RunMigrations(db *gorm.DB) error {

	migrations := make([]func() error, 3)

	// initial migration - creates tables
	migrations[0] = func() error {
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS service_instance_details (
			  id varchar(255) NOT NULL DEFAULT '',
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  name varchar(255) DEFAULT NULL,
			  location varchar(255) DEFAULT NULL,
			  url varchar(255) DEFAULT NULL,
			  other_details text,
			  service_id varchar(255) DEFAULT NULL,
			  plan_id varchar(255) DEFAULT NULL,
			  space_guid varchar(255) DEFAULT NULL,
			  organization_guid varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS service_binding_credentials (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  other_details text,
			  service_id varchar(255) DEFAULT NULL,
			  service_instance_id varchar(255) DEFAULT NULL,
			  binding_id varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id),
			  KEY idx_service_binding_credentials_deleted_at (deleted_at)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS provision_request_details (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  service_instance_id varchar(255) DEFAULT NULL,
			  request_details varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id),
			  KEY idx_provision_request_details_deleted_at (deleted_at)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS plan_details (
			  id varchar(255) NOT NULL DEFAULT '',
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  service_id varchar(255) DEFAULT NULL,
			  name varchar(255) DEFAULT NULL,
			  features text,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  migration_id int(10) DEFAULT NULL,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}
		return nil
	}

	// adds CloudOperation table
	migrations[1] = func() error {
		if err := db.Exec(`CREATE TABLE IF NOT EXISTS cloud_operations (
			  id int(10) unsigned NOT NULL AUTO_INCREMENT,
			  created_at timestamp NULL DEFAULT NULL,
			  updated_at timestamp NULL DEFAULT NULL,
			  deleted_at timestamp NULL DEFAULT NULL,
			  name varchar(255) DEFAULT NULL,
			  status varchar(255) DEFAULT NULL,
			  operation_type varchar(255) DEFAULT NULL,
			  error_message text,
			  insert_time varchar(255) DEFAULT NULL,
			  start_time varchar(255) DEFAULT NULL,
			  target_id varchar(255) DEFAULT NULL,
			  target_link varchar(255) DEFAULT NULL,
			  service_id varchar(255) DEFAULT NULL,
			  service_instance_id varchar(255) DEFAULT NULL,
			  PRIMARY KEY (id)
			) ENGINE=InnoDB DEFAULT CHARSET=utf8`).Error; err != nil {
			return err
		}

		// copy provision request details into service instance details
		serviceAccount := make(map[string]string)
		if err := json.Unmarshal([]byte(models.GetServiceAccountJson()), &serviceAccount); err != nil {
			return fmt.Errorf("Could not unmarshal service account details. %v", err)
		}

		cfg, err := utils.GetAuthedConfig()
		if err != nil {
			return fmt.Errorf("Error getting authorized http client: %s", err)
		}

		prs := []models.ProvisionRequestDetails{}
		if err := db.Find(&prs).Error; err != nil {
			return err
		}

		for _, pr := range prs {
			var si models.ServiceInstanceDetails
			if err := db.Where("id = ?", pr.ServiceInstanceId).First(&si).Error; err != nil {
				return err
			}
			od := make(map[string]string)
			if err := json.Unmarshal([]byte(pr.RequestDetails), &od); err != nil {
				return err
			}
			newOd := make(map[string]string)

			// cloudsql
			svc, err := broker.GetServiceById(si.ServiceId)
			if err != nil {
				return err
			}

			switch svc.Name {
			case models.CloudsqlMySQLName:
				newOd["instance_name"] = od["instance_name"]
				newOd["database_name"] = od["database_name"]

				sqlService, err := googlecloudsql.New(cfg.Client(context.Background()))
				if err != nil {
					return fmt.Errorf("Error creating new CloudSQL Client: %s", err)
				}
				dbService := googlecloudsql.NewInstancesService(sqlService)
				clouddb, err := dbService.Get(serviceAccount["project_id"], od["instance_name"]).Do()
				if err != nil {
					return fmt.Errorf("Error getting instance from api: %s", err)
				}
				newOd["host"] = clouddb.IpAddresses[0].IpAddress

			// bigquery
			case models.BigqueryName:
				newOd["dataset_id"] = od["name"]
			// ml apis
			case models.MlName:
				// n/a
			// storage
			case models.StorageName:
				newOd["bucket_name"] = od["name"]

			// pubsub
			case models.PubsubName:
				newOd["topic_name"] = od["topic_name"]
				newOd["subscription_name"] = od["subscription_name"]
			default:
				return fmt.Errorf("unrecognized service: %s", si.ServiceId)
			}

			odBytes, err := json.Marshal(&newOd)
			if err != nil {
				return err
			}
			si.OtherDetails = string(odBytes)
			if err := db.Save(&si).Error; err != nil {
				return err
			}
		}

		return nil
	}

	// drops plan details table
	migrations[2] = func() error {
		// NOOP migration, this was used to drop the plan_details table, but
		// there's more of a disincentive than incentive to do that because it could
		// leave operators wiping out plain details accidentally and not being able
		// to recover if they don't follow the upgrade path.
		return nil
	}

	var lastMigrationNumber = -1

	// if we've run any migrations before, we should have a migrations table, so find the last one we ran
	if db.HasTable("migrations") {
		var storedMigrations []models.Migration
		if err := db.Order("migration_id desc").Find(&storedMigrations).Error; err != nil {
			return fmt.Errorf("Error getting last migration id even though migration table exists: %s", err)
		}
		lastMigrationNumber = storedMigrations[0].MigrationId
	}

	// starting from the last migration we ran + 1, run migrations until we are current
	for i := lastMigrationNumber + 1; i < len(migrations); i++ {
		tx := db.Begin()
		err := migrations[i]()
		if err != nil {
			tx.Rollback()

			return err
		} else {
			newMigration := models.Migration{
				MigrationId: i,
			}
			if err := db.Save(&newMigration).Error; err != nil {
				tx.Rollback()
				return err
			} else {
				tx.Commit()
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package regenerate

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	helper "github.com/getgauge/html-report/test_helper"
)

var templateBasePath, _ = filepath.Abs(filepath.Join("..", "themes", "default"))

func TestEndToEndHTMLGenerationFromSavedResult(t *testing.T) {
	expectedFiles := []string{"index.html", "example.html", "js/search_index.js"}
	reportDir := filepath.Join("_testdata", "e2e")
	inputFile := filepath.Join("_testdata", "last_run_result")

	RegenerateReport(inputFile, reportDir, templateBasePath, "")
	for _, expectedFile := range expectedFiles {
		gotContent, err := ioutil.ReadFile(filepath.Join(reportDir, expectedFile))
		if err != nil {
			t.Errorf("Error reading generated HTML file: %s", err.Error())
		}
		wantContent, err := ioutil.ReadFile(filepath.Join("_testdata", "expectedE2E", "simpleSuiteRes", expectedFile))
		if err != nil {
			t.Errorf("Error reading expected HTML file: %s", err.Error())
		}
		got := helper.RemoveNewline(string(gotContent))
		want := helper.RemoveNewline(string(wantContent))
		helper.AssertEqual(want, got, expectedFile, t)
	}
	cleanUp(t, reportDir)
}

func cleanUp(t *testing.T, reportDir string) {
	s, err := filepath.Glob(filepath.Join(reportDir, "*"))
	if err != nil {
		t.Error(err)
	}
	for _, f := range s {
		if f != filepath.Join(reportDir, ".gitkeep") {
			os.RemoveAll(f)
		}
	}
}
<commit_msg>fix method rename<commit_after>package regenerate

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	helper "github.com/getgauge/html-report/test_helper"
)

var templateBasePath, _ = filepath.Abs(filepath.Join("..", "themes", "default"))

func TestEndToEndHTMLGenerationFromSavedResult(t *testing.T) {
	expectedFiles := []string{"index.html", "example.html", "js/search_index.js"}
	reportDir := filepath.Join("_testdata", "e2e")
	inputFile := filepath.Join("_testdata", "last_run_result")

	Report(inputFile, reportDir, templateBasePath, "")
	for _, expectedFile := range expectedFiles {
		gotContent, err := ioutil.ReadFile(filepath.Join(reportDir, expectedFile))
		if err != nil {
			t.Errorf("Error reading generated HTML file: %s", err.Error())
		}
		wantContent, err := ioutil.ReadFile(filepath.Join("_testdata", "expectedE2E", "simpleSuiteRes", expectedFile))
		if err != nil {
			t.Errorf("Error reading expected HTML file: %s", err.Error())
		}
		got := helper.RemoveNewline(string(gotContent))
		want := helper.RemoveNewline(string(wantContent))
		helper.AssertEqual(want, got, expectedFile, t)
	}
	cleanUp(t, reportDir)
}

func cleanUp(t *testing.T, reportDir string) {
	s, err := filepath.Glob(filepath.Join(reportDir, "*"))
	if err != nil {
		t.Error(err)
	}
	for _, f := range s {
		if f != filepath.Join(reportDir, ".gitkeep") {
			os.RemoveAll(f)
		}
	}
}
<|endoftext|>


<commit_before>package storage

import (
	"testing"

	"github.com/docker/distribution"
	"github.com/docker/distribution/context"
	"github.com/docker/distribution/reference"
	"github.com/docker/distribution/registry/storage/driver/inmemory"
)

type tagsTestEnv struct {
	ts  distribution.TagService
	ctx context.Context
}

func testTagStore(t *testing.T) *tagsTestEnv {
	ctx := context.Background()
	d := inmemory.New()
	reg, err := NewRegistry(ctx, d)
	if err != nil {
		t.Fatal(err)
	}

	repoRef, _ := reference.ParseNamed("a/b")
	repo, err := reg.Repository(ctx, repoRef)
	if err != nil {
		t.Fatal(err)
	}

	return &tagsTestEnv{
		ctx: ctx,
		ts:  repo.Tags(ctx),
	}
}

func TestTagStoreTag(t *testing.T) {
	env := testTagStore(t)
	tags := env.ts
	ctx := env.ctx

	d := distribution.Descriptor{}
	err := tags.Tag(ctx, "latest", d)
	if err == nil {
		t.Errorf("unexpected error putting malformed descriptor : %s", err)
	}

	d.Digest = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	err = tags.Tag(ctx, "latest", d)
	if err != nil {
		t.Error(err)
	}

	d1, err := tags.Get(ctx, "latest")
	if err != nil {
		t.Error(err)
	}

	if d1.Digest != d.Digest {
		t.Error("put and get digest differ")
	}

	// Overwrite existing
	d.Digest = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
	err = tags.Tag(ctx, "latest", d)
	if err != nil {
		t.Error(err)
	}

	d1, err = tags.Get(ctx, "latest")
	if err != nil {
		t.Error(err)
	}

	if d1.Digest != d.Digest {
		t.Error("put and get digest differ")
	}
}

func TestTagStoreUnTag(t *testing.T) {
	env := testTagStore(t)
	tags := env.ts
	ctx := env.ctx
	desc := distribution.Descriptor{Digest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}

	err := tags.Untag(ctx, "latest")
	if err == nil {
		t.Errorf("Expected error untagging non-existant tag")
	}

	err = tags.Tag(ctx, "latest", desc)
	if err != nil {
		t.Error(err)
	}

	err = tags.Untag(ctx, "latest")
	if err != nil {
		t.Error(err)
	}

	_, err = tags.Get(ctx, "latest")
	if err == nil {
		t.Error("Expected error getting untagged tag")
	}
}

func TestTagStoreAll(t *testing.T) {
	env := testTagStore(t)
	tagStore := env.ts
	ctx := env.ctx

	alpha := "abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < len(alpha); i++ {
		tag := alpha[i]
		desc := distribution.Descriptor{Digest: "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}
		err := tagStore.Tag(ctx, string(tag), desc)
		if err != nil {
			t.Error(err)
		}
	}

	all, err := tagStore.All(ctx)
	if err != nil {
		t.Error(err)
	}
	if len(all) != len(alpha) {
		t.Errorf("Unexpected count returned from enumerate")
	}

	for i, c := range all {
		if c != string(alpha[i]) {
			t.Errorf("unexpected tag in enumerate %s", c)
		}
	}

	removed := "a"
	err = tagStore.Untag(ctx, removed)
	if err != nil {
		t.Error(err)
	}

	all, err = tagStore.All(ctx)
	if err != nil {
		t.Error(err)
	}
	for _, tag := range all {
		if tag == removed {
			t.Errorf("unexpected tag in enumerate %s", removed)
		}
	}

}

func TestTagLookup(t *testing.T) {
	env := testTagStore(t)
	tagStore := env.ts
	ctx := env.ctx

	descA := distribution.Descriptor{Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
	desc0 := distribution.Descriptor{Digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"}

	tags, err := tagStore.Lookup(ctx, descA)
	if err != nil {
		t.Fatal(err)
	}
	if len(tags) != 0 {
		t.Fatalf("Lookup returned > 0 tags from empty store")
	}

	err = tagStore.Tag(ctx, "a", descA)
	if err != nil {
		t.Fatal(err)
	}

	err = tagStore.Tag(ctx, "b", descA)
	if err != nil {
		t.Fatal(err)
	}

	err = tagStore.Tag(ctx, "0", desc0)
	if err != nil {
		t.Fatal(err)
	}

	err = tagStore.Tag(ctx, "1", desc0)
	if err != nil {
		t.Fatal(err)
	}

	tags, err = tagStore.Lookup(ctx, descA)
	if err != nil {
		t.Fatal(err)
	}

	if len(tags) != 2 {
		t.Errorf("Lookup of descA returned %d tags, expected 2", len(tags))
	}

	tags, err = tagStore.Lookup(ctx, desc0)
	if err != nil {
		t.Fatal(err)
	}

	if len(tags) != 2 {
		t.Errorf("Lookup of descB returned %d tags, expected 2", len(tags))
	}

}
<commit_msg>compare error output in tagstore unit test<commit_after>package storage

import (
	"testing"

	"github.com/docker/distribution"
	"github.com/docker/distribution/context"
	"github.com/docker/distribution/reference"
	"github.com/docker/distribution/registry/storage/driver/inmemory"
)

type tagsTestEnv struct {
	ts  distribution.TagService
	ctx context.Context
}

func testTagStore(t *testing.T) *tagsTestEnv {
	ctx := context.Background()
	d := inmemory.New()
	reg, err := NewRegistry(ctx, d)
	if err != nil {
		t.Fatal(err)
	}

	repoRef, _ := reference.ParseNamed("a/b")
	repo, err := reg.Repository(ctx, repoRef)
	if err != nil {
		t.Fatal(err)
	}

	return &tagsTestEnv{
		ctx: ctx,
		ts:  repo.Tags(ctx),
	}
}

func TestTagStoreTag(t *testing.T) {
	env := testTagStore(t)
	tags := env.ts
	ctx := env.ctx

	d := distribution.Descriptor{}
	err := tags.Tag(ctx, "latest", d)
	if err == nil {
		t.Errorf("unexpected error putting malformed descriptor : %s", err)
	}

	d.Digest = "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
	err = tags.Tag(ctx, "latest", d)
	if err != nil {
		t.Error(err)
	}

	d1, err := tags.Get(ctx, "latest")
	if err != nil {
		t.Error(err)
	}

	if d1.Digest != d.Digest {
		t.Error("put and get digest differ")
	}

	// Overwrite existing
	d.Digest = "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
	err = tags.Tag(ctx, "latest", d)
	if err != nil {
		t.Error(err)
	}

	d1, err = tags.Get(ctx, "latest")
	if err != nil {
		t.Error(err)
	}

	if d1.Digest != d.Digest {
		t.Error("put and get digest differ")
	}
}

func TestTagStoreUnTag(t *testing.T) {
	env := testTagStore(t)
	tags := env.ts
	ctx := env.ctx
	desc := distribution.Descriptor{Digest: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}

	err := tags.Untag(ctx, "latest")
	if err == nil {
		t.Errorf("Expected error untagging non-existant tag")
	}

	err = tags.Tag(ctx, "latest", desc)
	if err != nil {
		t.Error(err)
	}

	err = tags.Untag(ctx, "latest")
	if err != nil {
		t.Error(err)
	}

	errExpect := distribution.ErrTagUnknown{Tag: "latest"}.Error()
	_, err = tags.Get(ctx, "latest")
	if err == nil || err.Error() != errExpect {
		t.Error("Expected error getting untagged tag")
	}
}

func TestTagStoreAll(t *testing.T) {
	env := testTagStore(t)
	tagStore := env.ts
	ctx := env.ctx

	alpha := "abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < len(alpha); i++ {
		tag := alpha[i]
		desc := distribution.Descriptor{Digest: "sha256:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}
		err := tagStore.Tag(ctx, string(tag), desc)
		if err != nil {
			t.Error(err)
		}
	}

	all, err := tagStore.All(ctx)
	if err != nil {
		t.Error(err)
	}
	if len(all) != len(alpha) {
		t.Errorf("Unexpected count returned from enumerate")
	}

	for i, c := range all {
		if c != string(alpha[i]) {
			t.Errorf("unexpected tag in enumerate %s", c)
		}
	}

	removed := "a"
	err = tagStore.Untag(ctx, removed)
	if err != nil {
		t.Error(err)
	}

	all, err = tagStore.All(ctx)
	if err != nil {
		t.Error(err)
	}
	for _, tag := range all {
		if tag == removed {
			t.Errorf("unexpected tag in enumerate %s", removed)
		}
	}

}

func TestTagLookup(t *testing.T) {
	env := testTagStore(t)
	tagStore := env.ts
	ctx := env.ctx

	descA := distribution.Descriptor{Digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}
	desc0 := distribution.Descriptor{Digest: "sha256:0000000000000000000000000000000000000000000000000000000000000000"}

	tags, err := tagStore.Lookup(ctx, descA)
	if err != nil {
		t.Fatal(err)
	}
	if len(tags) != 0 {
		t.Fatalf("Lookup returned > 0 tags from empty store")
	}

	err = tagStore.Tag(ctx, "a", descA)
	if err != nil {
		t.Fatal(err)
	}

	err = tagStore.Tag(ctx, "b", descA)
	if err != nil {
		t.Fatal(err)
	}

	err = tagStore.Tag(ctx, "0", desc0)
	if err != nil {
		t.Fatal(err)
	}

	err = tagStore.Tag(ctx, "1", desc0)
	if err != nil {
		t.Fatal(err)
	}

	tags, err = tagStore.Lookup(ctx, descA)
	if err != nil {
		t.Fatal(err)
	}

	if len(tags) != 2 {
		t.Errorf("Lookup of descA returned %d tags, expected 2", len(tags))
	}

	tags, err = tagStore.Lookup(ctx, desc0)
	if err != nil {
		t.Fatal(err)
	}

	if len(tags) != 2 {
		t.Errorf("Lookup of descB returned %d tags, expected 2", len(tags))
	}

}
<|endoftext|>


<commit_before>package server

type ServerRes struct {
	Server       string
	IsQueued     bool
	ErrorMessage string
}
<commit_msg>Add the missing Links field to the ServerRes type.<commit_after>package server

import (
	"github.com/centurylinkcloud/clc-go-cli/models"
)

type ServerRes struct {
	Server       string
	IsQueued     bool
	ErrorMessage string
	Links        []models.LinkEntity
}
<|endoftext|>