<commit_before>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package account_test

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"testing"

	"github.com/bitmark-inc/bitmarkd/account"
	"github.com/bitmark-inc/bitmarkd/fault"
)

// Test account functionality

type accountTest struct {
	algorithm     int
	testnet       bool
	zero          bool
	publicKey     []byte
	base58Account string
}

// Valid account
var testAccount = []accountTest{
	{
		algorithm:     account.ED25519,
		testnet:       false,
		zero:          false,
		publicKey:     decodeHex("60b3c6e20cfff7091a86488b1656b96ec0a2f69907e2c035175918f42c37d72e"),
		base58Account: "anF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLCj",
	},
	{
		algorithm:     account.ED25519,
		testnet:       true,
		zero:          false,
		publicKey:     decodeHex("731114267f15754a5fce4aaed8380b28aff25af7b378b011d92ef7b3f08910db"),
		base58Account: "eopaSeB7uiSVMdAmTrijq3W2MCWA5KHZrZvm5QLFGRVd3oWNe2",
	},
	{
		algorithm:     account.ED25519,
		testnet:       true,
		zero:          false,
		publicKey:     decodeHex("cb6ff605f79deba3deb0c5122e40359a258481c151dffc176a2da5e8bc87cd2e"),
		base58Account: "fUjtNvmUJn7yJ7PVP7NT2FZbKDrudFxLVBHkwLJFgKWmGsPNVi",
	},
	{
		algorithm:     account.ED25519,
		testnet:       true,
		zero:          true,
		publicKey:     decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
		base58Account: "dw9MQXcC5rJZb3QE1nz86PiQAheMP1dx9M3dr52tT8NNs14m33",
	},
	{
		algorithm:     account.ED25519,
		testnet:       false,
		zero:          true,
		publicKey:     decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
		base58Account: "a3ezwdYVEVrHwszQrYzDTCAZwUD3yKtNsCq9YhEu97bPaGAKy1",
	},
	{
		algorithm:     account.Nothing,
		testnet:       false,
		zero:          false,
		publicKey:     decodeHex("12fa"),
		base58Account: "3MvykBZzN",
	},
	{
		algorithm:     account.Nothing,
		testnet:       false,
		zero:          true,
		publicKey:     decodeHex("0000"),
		base58Account: "3CUwbPENE",
	},
}

type invalid struct {
	str string
	err error
}

// Invalid account
var testInvalidAccountFromBase58 = []invalid{
	{"3gLJjLSociTmf4kgL3ztUK;tgADFvg9yjXt1jFbEx9KgpEEAFn", fault.ErrCannotDecodeAccount}, // invalid base58 string
	{"anF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLDj", fault.ErrChecksumMismatch},    // checksum mismatch
	{"WjbRFkA9dhmMKnKTuufZ1sVD4E4H1NRnsmwjMKNHHRSCvDm5bXPV", fault.ErrInvalidKeyType},    // undefined key algorithm
	{"YqVxD4vazrrnxnLH2MzCHJedPPz1VKHnKbVfya39nF96ABAYes", fault.ErrNotPublicKey},        // private key
	{"anF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLC", fault.ErrNotPublicKey},         // truncated
	{"nF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLCj", fault.ErrNotPublicKey},         // truncated
}

// show manually created accounts
// this has to be changed if account.go is modified
// it is used to print the base58Account for testAccount above
func TestValid(t *testing.T) {

loop:
	for index, test := range testAccount {
		testnet := 0x00
		if test.testnet {
			testnet = 0x02
		}

		buffer := []byte{byte(test.algorithm<<4 | 0x01 | testnet)}
		buffer = append(buffer, test.publicKey...)
		account, err := account.AccountFromBytes(buffer)
		if nil != err {
			t.Errorf("%d: Create account from bytes failed: %s", index, err)
			continue loop
		}
		t.Logf("%d: result: %s", index, account)
		t.Logf("%d:    hex: %x", index, account.Bytes())

		if !bytes.Equal(buffer, account.Bytes()) {
			t.Errorf("%d: account bytes: %x does not match: %x", index, account.Bytes(), buffer)
		}

		accountIsZero := true
	check_for_zero:
		for _, b := range account.PublicKeyBytes() {
			if 0 != b {
				accountIsZero = false
				break check_for_zero
			}
		}
		if test.zero {
			if !accountIsZero {
				t.Errorf("%d: account bytes: %x not zero, but should be zero", index, account.PublicKeyBytes())
			}
			if !account.IsZero() {
				t.Errorf("%d: account.IsZero() incorrectly returned false", index)
			}
		} else {
			if accountIsZero {
				t.Errorf("%d: account bytes: %x are all zero, but should not be", index, account.PublicKeyBytes())
			}
			if account.IsZero() {
				t.Errorf("%d: account.IsZero() incorrectly returned true", index)
			}
		}
	}
}

// From valid base58 string to account
func TestValidBase58(t *testing.T) {
loop:
	for index, test := range testAccount {
		acc, err := account.AccountFromBase58(test.base58Account)
		if nil != err {
			t.Errorf("%d: from base58 error: %s", index, err)
			continue loop
		}
		if acc.IsTesting() != test.testnet {
			t.Errorf("%d: from base58 testnet: %t  expected: %t", index, acc.IsTesting(), test.testnet)
		}
		if acc.KeyType() != test.algorithm {
			t.Errorf("%d: from base58 type: %d  expected: %d", index, acc.KeyType(), test.algorithm)
		}
		if !bytes.Equal(acc.PublicKeyBytes(), test.publicKey) {
			t.Errorf("%d: from base58 pubkey: %x  expected %x", index, acc.PublicKeyBytes(), test.publicKey)
		}
		if acc.String() != test.base58Account {
			t.Errorf("%d: to base58: got: %s  expected %s", index, acc, test.base58Account)
		}

		// test unmarshal JSON
		j := `"` + test.base58Account + `"`
		var a account.Account
		err = json.Unmarshal([]byte(j), &a)
		if nil != err {
			t.Errorf("%d: from JSON string error: %s", index, err)
			continue loop
		}
		t.Logf("%d: from JSON: %#v", index, a)

		buffer, err := json.Marshal(a)
		t.Logf("%d: account to JSON: %s", index, buffer)
		if j != string(buffer) {
			t.Errorf("%d: marshal JSON:failed: expected %s  actual: %s", index, j, buffer)
		}

	}
}

// Test invalid account parsing
// From account base58 encoded to account
func TestInvalidBase58(t *testing.T) {
	for index, test := range testInvalidAccountFromBase58 {
		_, err := account.AccountFromBase58(test.str)
		if test.err != err {
			t.Errorf("invalid base58 string: %d failed: expected: %q actual: %q", index, test.err, err)
		}
	}
}

// Decode the hex string and return []byte.
//
// This is only used in the tests as the source is pre-prepared, so that there won't be any error
func decodeHex(hexStr string) []byte {
	b, err := hex.DecodeString(hexStr)
	if err != nil {
		panic(err)
	}
	return b
}
<commit_msg>[account] - Fixes this value of err is never used (SA4006)<commit_after>// Copyright (c) 2014-2019 Bitmark Inc.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package account_test

import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"testing"

	"github.com/bitmark-inc/bitmarkd/account"
	"github.com/bitmark-inc/bitmarkd/fault"
)

// Test account functionality

type accountTest struct {
	algorithm     int
	testnet       bool
	zero          bool
	publicKey     []byte
	base58Account string
}

// Valid account
var testAccount = []accountTest{
	{
		algorithm:     account.ED25519,
		testnet:       false,
		zero:          false,
		publicKey:     decodeHex("60b3c6e20cfff7091a86488b1656b96ec0a2f69907e2c035175918f42c37d72e"),
		base58Account: "anF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLCj",
	},
	{
		algorithm:     account.ED25519,
		testnet:       true,
		zero:          false,
		publicKey:     decodeHex("731114267f15754a5fce4aaed8380b28aff25af7b378b011d92ef7b3f08910db"),
		base58Account: "eopaSeB7uiSVMdAmTrijq3W2MCWA5KHZrZvm5QLFGRVd3oWNe2",
	},
	{
		algorithm:     account.ED25519,
		testnet:       true,
		zero:          false,
		publicKey:     decodeHex("cb6ff605f79deba3deb0c5122e40359a258481c151dffc176a2da5e8bc87cd2e"),
		base58Account: "fUjtNvmUJn7yJ7PVP7NT2FZbKDrudFxLVBHkwLJFgKWmGsPNVi",
	},
	{
		algorithm:     account.ED25519,
		testnet:       true,
		zero:          true,
		publicKey:     decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
		base58Account: "dw9MQXcC5rJZb3QE1nz86PiQAheMP1dx9M3dr52tT8NNs14m33",
	},
	{
		algorithm:     account.ED25519,
		testnet:       false,
		zero:          true,
		publicKey:     decodeHex("0000000000000000000000000000000000000000000000000000000000000000"),
		base58Account: "a3ezwdYVEVrHwszQrYzDTCAZwUD3yKtNsCq9YhEu97bPaGAKy1",
	},
	{
		algorithm:     account.Nothing,
		testnet:       false,
		zero:          false,
		publicKey:     decodeHex("12fa"),
		base58Account: "3MvykBZzN",
	},
	{
		algorithm:     account.Nothing,
		testnet:       false,
		zero:          true,
		publicKey:     decodeHex("0000"),
		base58Account: "3CUwbPENE",
	},
}

type invalid struct {
	str string
	err error
}

// Invalid account
var testInvalidAccountFromBase58 = []invalid{
	{"3gLJjLSociTmf4kgL3ztUK;tgADFvg9yjXt1jFbEx9KgpEEAFn", fault.ErrCannotDecodeAccount}, // invalid base58 string
	{"anF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLDj", fault.ErrChecksumMismatch},    // checksum mismatch
	{"WjbRFkA9dhmMKnKTuufZ1sVD4E4H1NRnsmwjMKNHHRSCvDm5bXPV", fault.ErrInvalidKeyType},    // undefined key algorithm
	{"YqVxD4vazrrnxnLH2MzCHJedPPz1VKHnKbVfya39nF96ABAYes", fault.ErrNotPublicKey},        // private key
	{"anF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLC", fault.ErrNotPublicKey},         // truncated
	{"nF8SWxSRY5vnN3Bbyz9buRYW1hfCAAZxfbv8Fw9SFXaktvLCj", fault.ErrNotPublicKey},         // truncated
}

// show manually created accounts
// this has to be changed if account.go is modified
// it is used to print the base58Account for testAccount above
func TestValid(t *testing.T) {

loop:
	for index, test := range testAccount {
		testnet := 0x00
		if test.testnet {
			testnet = 0x02
		}

		buffer := []byte{byte(test.algorithm<<4 | 0x01 | testnet)}
		buffer = append(buffer, test.publicKey...)
		account, err := account.AccountFromBytes(buffer)
		if nil != err {
			t.Errorf("%d: Create account from bytes failed: %s", index, err)
			continue loop
		}
		t.Logf("%d: result: %s", index, account)
		t.Logf("%d:    hex: %x", index, account.Bytes())

		if !bytes.Equal(buffer, account.Bytes()) {
			t.Errorf("%d: account bytes: %x does not match: %x", index, account.Bytes(), buffer)
		}

		accountIsZero := true
	check_for_zero:
		for _, b := range account.PublicKeyBytes() {
			if 0 != b {
				accountIsZero = false
				break check_for_zero
			}
		}
		if test.zero {
			if !accountIsZero {
				t.Errorf("%d: account bytes: %x not zero, but should be zero", index, account.PublicKeyBytes())
			}
			if !account.IsZero() {
				t.Errorf("%d: account.IsZero() incorrectly returned false", index)
			}
		} else {
			if accountIsZero {
				t.Errorf("%d: account bytes: %x are all zero, but should not be", index, account.PublicKeyBytes())
			}
			if account.IsZero() {
				t.Errorf("%d: account.IsZero() incorrectly returned true", index)
			}
		}
	}
}

// From valid base58 string to account
func TestValidBase58(t *testing.T) {
loop:
	for index, test := range testAccount {
		acc, err := account.AccountFromBase58(test.base58Account)
		if nil != err {
			t.Errorf("%d: from base58 error: %s", index, err)
			continue loop
		}
		if acc.IsTesting() != test.testnet {
			t.Errorf("%d: from base58 testnet: %t  expected: %t", index, acc.IsTesting(), test.testnet)
		}
		if acc.KeyType() != test.algorithm {
			t.Errorf("%d: from base58 type: %d  expected: %d", index, acc.KeyType(), test.algorithm)
		}
		if !bytes.Equal(acc.PublicKeyBytes(), test.publicKey) {
			t.Errorf("%d: from base58 pubkey: %x  expected %x", index, acc.PublicKeyBytes(), test.publicKey)
		}
		if acc.String() != test.base58Account {
			t.Errorf("%d: to base58: got: %s  expected %s", index, acc, test.base58Account)
		}

		// test unmarshal JSON
		j := `"` + test.base58Account + `"`
		var a account.Account
		err = json.Unmarshal([]byte(j), &a)
		if nil != err {
			t.Errorf("%d: from JSON string error: %s", index, err)
			continue loop
		}
		t.Logf("%d: from JSON: %#v", index, a)

		buffer, _ := json.Marshal(a)
		t.Logf("%d: account to JSON: %s", index, buffer)
		if j != string(buffer) {
			t.Errorf("%d: marshal JSON:failed: expected %s  actual: %s", index, j, buffer)
		}

	}
}

// Test invalid account parsing
// From account base58 encoded to account
func TestInvalidBase58(t *testing.T) {
	for index, test := range testInvalidAccountFromBase58 {
		_, err := account.AccountFromBase58(test.str)
		if test.err != err {
			t.Errorf("invalid base58 string: %d failed: expected: %q actual: %q", index, test.err, err)
		}
	}
}

// Decode the hex string and return []byte.
//
// This is only used in the tests as the source is pre-prepared, so that there won't be any error
func decodeHex(hexStr string) []byte {
	b, err := hex.DecodeString(hexStr)
	if err != nil {
		panic(err)
	}
	return b
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 config_test

import (
	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/store/scope"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestScopedServiceString(t *testing.T) {
	tests := []struct {
		desc                        string
		fqpath                      string
		path                        []string
		websiteID, groupID, storeID int64
		err                         error
	}{
		{
			"Default ScopedGetter should return default scope",
			scope.StrDefault.FQPath("0", "a/b/c"), []string{"a/b/c"}, 0, 0, 0, nil,
		},
		{
			"Website ID 1 ScopedGetter should fall back to default scope",
			scope.StrDefault.FQPath("0", "a/b/c"), []string{"a/b/c"}, 1, 0, 0, nil,
		},
		{
			"Website ID 10 + Group ID 12 ScopedGetter should fall back to website 10 scope",
			scope.StrWebsites.FQPath("10", "a/b/c"), []string{"a/b/c"}, 10, 12, 0, nil,
		},
		{
			"Path consists of only two elements which is incorrect",
			scope.StrDefault.FQPath("0", "a/b/c"), []string{"a", "b"}, 0, 0, 0, config.ErrPathEmpty,
		},
	}
	for _, test := range tests {

		cg := config.NewMockGetter(config.WithMockValues(config.MockPV{
			test.fqpath: "Gopher",
		}))

		sg := cg.NewScoped(test.websiteID, test.groupID, test.storeID)
		s, err := sg.String(test.path...)

		if test.err != nil {
			assert.Empty(t, s, test.desc)
			assert.EqualError(t, err, test.err.Error(), test.desc)
			continue
		}
		assert.NoError(t, err, test.desc)
		assert.Exactly(t, "Gopher", s, test.desc)
	}
}

func TestScopedServiceBool(t *testing.T) {
	t.Log("TODO")
}

func TestScopedServiceFloat64(t *testing.T) {
	t.Log("TODO")
}
func TestScopedServiceInt(t *testing.T) {
	t.Log("TODO")
}
func TestScopedServiceDateTime(t *testing.T) {
	t.Log("TODO")
}
<commit_msg>config: TestScopedService tests now all types. some still missing<commit_after>// Copyright 2015-2016, Cyrill @ Schumacher.fm and the CoreStore contributors
//
// 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 config_test

import (
	"github.com/corestoreio/csfw/config"
	"github.com/corestoreio/csfw/store/scope"
	"github.com/stretchr/testify/assert"
	"testing"
	"time"
)

func TestScopedServiceScope(t *testing.T) {
	tests := []struct {
		websiteID, groupID, storeID int64
		wantScope                   scope.Scope
		wantID                      int64
	}{
		{0, 0, 0, scope.DefaultID, 0},
		{1, 0, 0, scope.WebsiteID, 1},
		{1, 2, 0, scope.GroupID, 2},
		{1, 2, 3, scope.StoreID, 3},
		{0, 0, 3, scope.StoreID, 3},
		{0, 2, 0, scope.GroupID, 2},
	}
	for i, test := range tests {
		sg := config.NewMockGetter().NewScoped(test.websiteID, test.groupID, test.storeID)
		haveScope, haveID := sg.Scope()
		assert.Exactly(t, test.wantScope, haveScope, "Index %d", i)
		assert.Exactly(t, test.wantID, haveID, "Index %d", i)
	}
}

func TestScopedService(t *testing.T) {
	tests := []struct {
		desc                        string
		fqpath                      string
		path                        []string
		websiteID, groupID, storeID int64
		err                         error
	}{
		{
			"Default ScopedGetter should return default scope",
			scope.StrDefault.FQPath("0", "a/b/c"), []string{"a/b/c"}, 0, 0, 0, nil,
		},
		{
			"Website ID 1 ScopedGetter should fall back to default scope",
			scope.StrDefault.FQPath("0", "a/b/c"), []string{"a/b/c"}, 1, 0, 0, nil,
		},
		{
			"Website ID 10 + Group ID 12 ScopedGetter should fall back to website 10 scope",
			scope.StrWebsites.FQPath("10", "a/b/c"), []string{"a/b/c"}, 10, 12, 0, nil,
		},
		{
			"Website ID 10 + Group ID 12 + Store 22 ScopedGetter should fall back to website 10 scope",
			scope.StrWebsites.FQPath("10", "a/b/c"), []string{"a/b/c"}, 10, 12, 22, nil,
		},
		{
			"Website ID 10 + Group ID 12 + Store 22 ScopedGetter should return Store 22 scope",
			scope.StrStores.FQPath("22", "a/b/c"), []string{"a/b/c"}, 10, 12, 22, nil,
		},
		{
			"Website ID 10 + Group ID 12 + Store 42 ScopedGetter should return nothing",
			scope.StrStores.FQPath("22", "a/b/c"), []string{"a/b/c"}, 10, 12, 42, config.ErrKeyNotFound,
		},
		{
			"Path consists of only two elements which is incorrect",
			scope.StrDefault.FQPath("0", "a/b/c"), []string{"a", "b"}, 0, 0, 0, config.ErrPathEmpty,
		},
	}

	vals := []interface{}{"Gopher", true, float64(3.14159), int(2016), time.Now()}

	for vi, val := range vals {
		for _, test := range tests {

			cg := config.NewMockGetter(config.WithMockValues(config.MockPV{
				test.fqpath: val,
			}))

			sg := cg.NewScoped(test.websiteID, test.groupID, test.storeID)

			switch val.(type) {
			case string:
				s, err := sg.String(test.path...)
				testScopedService(t, s, test.desc, err, test.err)
			default:
				t.Fatalf("Unsupported type: %#v in index %d", val, vi)
			}
		}
	}
}

func testScopedService(t *testing.T, have, desc string, err, wantErr error) {
	if wantErr != nil {
		assert.Empty(t, have, desc)
		assert.EqualError(t, err, wantErr.Error(), desc)
		return
	}
	assert.NoError(t, err, desc)
	assert.Exactly(t, "Gopher", have, desc)
}

func BenchmarkScopedServiceStringDefault(b *testing.B) {
	for i := 0; i < b.N; i++ {

	}
}
func BenchmarkScopedServiceStringWebsite(b *testing.B) {
	for i := 0; i < b.N; i++ {

	}
}
func BenchmarkScopedServiceStringGroup(b *testing.B) {
	for i := 0; i < b.N; i++ {

	}
}
func BenchmarkScopedServiceStringStore(b *testing.B) {
	for i := 0; i < b.N; i++ {

	}
}
<|endoftext|>


<commit_before>package pushaction_test

import (
	"errors"
	"io/ioutil"
	"os"

	. "code.cloudfoundry.org/cli/actor/pushaction"
	"code.cloudfoundry.org/cli/actor/pushaction/pushactionfakes"
	"code.cloudfoundry.org/cli/actor/v2action"

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

var _ = Describe("Resources", func() {
	var (
		actor       *Actor
		fakeV2Actor *pushactionfakes.FakeV2Actor
	)

	BeforeEach(func() {
		fakeV2Actor = new(pushactionfakes.FakeV2Actor)
		actor = NewActor(fakeV2Actor)
	})

	Describe("CreateArchive", func() {
		var (
			config ApplicationConfig

			archivePath string
			executeErr  error

			resourcesToArchive []v2action.Resource
		)

		BeforeEach(func() {
			config = ApplicationConfig{
				Path: "some-path",
				DesiredApplication: v2action.Application{
					GUID: "some-app-guid",
				},
			}

			resourcesToArchive = []v2action.Resource{{Filename: "file1"}, {Filename: "file2"}}
			config.AllResources = resourcesToArchive
		})

		JustBeforeEach(func() {
			archivePath, executeErr = actor.CreateArchive(config)
		})

		Context("when the zipping is successful", func() {
			var fakeArchivePath string
			BeforeEach(func() {
				fakeArchivePath = "some-archive-path"
				fakeV2Actor.ZipResourcesReturns(fakeArchivePath, nil)
			})

			It("returns the path to the zip", func() {
				Expect(executeErr).ToNot(HaveOccurred())
				Expect(archivePath).To(Equal(fakeArchivePath))

				Expect(fakeV2Actor.ZipResourcesCallCount()).To(Equal(1))
				sourceDir, passedResources := fakeV2Actor.ZipResourcesArgsForCall(0)
				Expect(sourceDir).To(Equal("some-path"))
				Expect(passedResources).To(Equal(resourcesToArchive))
			})
		})

		Context("when creating the archive errors", func() {
			var expectedErr error

			BeforeEach(func() {
				expectedErr = errors.New("oh no")
				fakeV2Actor.ZipResourcesReturns("", expectedErr)
			})

			It("sends errors and returns true", func() {
				Expect(executeErr).To(MatchError(expectedErr))
			})
		})
	})

	Describe("UploadPackage", func() {
		var (
			config      ApplicationConfig
			archivePath string
			eventStream chan Event

			warnings   Warnings
			executeErr error
		)

		BeforeEach(func() {
			config = ApplicationConfig{
				DesiredApplication: v2action.Application{
					GUID: "some-app-guid",
				},
			}
			eventStream = make(chan Event)
		})

		AfterEach(func() {
			close(eventStream)
		})

		JustBeforeEach(func() {
			warnings, executeErr = actor.UploadPackage(config, archivePath, eventStream)
		})

		Context("when the archive can be accessed properly", func() {
			BeforeEach(func() {
				tmpfile, err := ioutil.TempFile("", "fake-archive")
				Expect(err).ToNot(HaveOccurred())
				_, err = tmpfile.Write([]byte("123456"))
				Expect(err).ToNot(HaveOccurred())
				Expect(tmpfile.Close()).ToNot(HaveOccurred())

				archivePath = tmpfile.Name()
			})

			AfterEach(func() {
				if archivePath != "" {
					os.Remove(archivePath)
				}
			})

			Context("when the upload is successful", func() {
				BeforeEach(func() {
					fakeV2Actor.UploadApplicationPackageReturns(v2action.Warnings{"upload-warning-1", "upload-warning-2"}, nil)

					go func() {
						defer GinkgoRecover()

						Eventually(eventStream).Should(Receive(Equal(UploadingApplication)))
						Eventually(eventStream).Should(Receive(Equal(UploadComplete)))
					}()
				})

				It("returns the warnings", func() {
					Expect(executeErr).ToNot(HaveOccurred())
					Expect(warnings).To(ConsistOf("upload-warning-1", "upload-warning-2"))

					Expect(fakeV2Actor.UploadApplicationPackageCallCount()).To(Equal(1))
					appGUID, existingResources, _, newResourcesLength := fakeV2Actor.UploadApplicationPackageArgsForCall(0)
					Expect(appGUID).To(Equal("some-app-guid"))
					Expect(existingResources).To(BeEmpty())
					Expect(newResourcesLength).To(BeNumerically("==", 6))
				})
			})

			Context("when the upload errors", func() {
				var expectedErr error

				BeforeEach(func() {
					expectedErr = errors.New("I can't let you do that starfox")
					fakeV2Actor.UploadApplicationPackageReturns(v2action.Warnings{"upload-warning-1", "upload-warning-2"}, expectedErr)

					go func() {
						defer GinkgoRecover()

						Eventually(eventStream).Should(Receive(Equal(UploadingApplication)))
						Consistently(eventStream).ShouldNot(Receive())
					}()
				})

				It("returns the error and warnings", func() {
					Expect(executeErr).To(MatchError(expectedErr))
					Expect(warnings).To(ConsistOf("upload-warning-1", "upload-warning-2"))
				})
			})
		})

		Context("when the archive returns any access errors", func() {
			It("returns the error", func() {
				_, ok := executeErr.(*os.PathError)
				Expect(ok).To(BeTrue())
			})
		})
	})
})
<commit_msg>handle race condition when reading eventStream<commit_after>package pushaction_test

import (
	"errors"
	"io/ioutil"
	"os"

	. "code.cloudfoundry.org/cli/actor/pushaction"
	"code.cloudfoundry.org/cli/actor/pushaction/pushactionfakes"
	"code.cloudfoundry.org/cli/actor/v2action"

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

var _ = Describe("Resources", func() {
	var (
		actor       *Actor
		fakeV2Actor *pushactionfakes.FakeV2Actor
	)

	BeforeEach(func() {
		fakeV2Actor = new(pushactionfakes.FakeV2Actor)
		actor = NewActor(fakeV2Actor)
	})

	Describe("CreateArchive", func() {
		var (
			config ApplicationConfig

			archivePath string
			executeErr  error

			resourcesToArchive []v2action.Resource
		)

		BeforeEach(func() {
			config = ApplicationConfig{
				Path: "some-path",
				DesiredApplication: v2action.Application{
					GUID: "some-app-guid",
				},
			}

			resourcesToArchive = []v2action.Resource{{Filename: "file1"}, {Filename: "file2"}}
			config.AllResources = resourcesToArchive
		})

		JustBeforeEach(func() {
			archivePath, executeErr = actor.CreateArchive(config)
		})

		Context("when the zipping is successful", func() {
			var fakeArchivePath string
			BeforeEach(func() {
				fakeArchivePath = "some-archive-path"
				fakeV2Actor.ZipResourcesReturns(fakeArchivePath, nil)
			})

			It("returns the path to the zip", func() {
				Expect(executeErr).ToNot(HaveOccurred())
				Expect(archivePath).To(Equal(fakeArchivePath))

				Expect(fakeV2Actor.ZipResourcesCallCount()).To(Equal(1))
				sourceDir, passedResources := fakeV2Actor.ZipResourcesArgsForCall(0)
				Expect(sourceDir).To(Equal("some-path"))
				Expect(passedResources).To(Equal(resourcesToArchive))
			})
		})

		Context("when creating the archive errors", func() {
			var expectedErr error

			BeforeEach(func() {
				expectedErr = errors.New("oh no")
				fakeV2Actor.ZipResourcesReturns("", expectedErr)
			})

			It("sends errors and returns true", func() {
				Expect(executeErr).To(MatchError(expectedErr))
			})
		})
	})

	Describe("UploadPackage", func() {
		var (
			config      ApplicationConfig
			archivePath string
			eventStream chan Event

			warnings   Warnings
			executeErr error
		)

		BeforeEach(func() {
			config = ApplicationConfig{
				DesiredApplication: v2action.Application{
					GUID: "some-app-guid",
				},
			}
			eventStream = make(chan Event)
		})

		AfterEach(func() {
			close(eventStream)
		})

		JustBeforeEach(func() {
			warnings, executeErr = actor.UploadPackage(config, archivePath, eventStream)
		})

		Context("when the archive can be accessed properly", func() {
			BeforeEach(func() {
				tmpfile, err := ioutil.TempFile("", "fake-archive")
				Expect(err).ToNot(HaveOccurred())
				_, err = tmpfile.Write([]byte("123456"))
				Expect(err).ToNot(HaveOccurred())
				Expect(tmpfile.Close()).ToNot(HaveOccurred())

				archivePath = tmpfile.Name()
			})

			AfterEach(func() {
				Expect(os.Remove(archivePath)).ToNot(HaveOccurred())
			})

			Context("when the upload is successful", func() {
				BeforeEach(func() {
					fakeV2Actor.UploadApplicationPackageReturns(v2action.Warnings{"upload-warning-1", "upload-warning-2"}, nil)

					go func() {
						defer GinkgoRecover()

						Eventually(eventStream).Should(Receive(Equal(UploadingApplication)))
						Eventually(eventStream).Should(Receive(Equal(UploadComplete)))
					}()
				})

				It("returns the warnings", func() {
					Expect(executeErr).ToNot(HaveOccurred())
					Expect(warnings).To(ConsistOf("upload-warning-1", "upload-warning-2"))

					Expect(fakeV2Actor.UploadApplicationPackageCallCount()).To(Equal(1))
					appGUID, existingResources, _, newResourcesLength := fakeV2Actor.UploadApplicationPackageArgsForCall(0)
					Expect(appGUID).To(Equal("some-app-guid"))
					Expect(existingResources).To(BeEmpty())
					Expect(newResourcesLength).To(BeNumerically("==", 6))
				})
			})

			Context("when the upload errors", func() {
				var (
					expectedErr error
					done        chan bool
				)

				BeforeEach(func() {
					expectedErr = errors.New("I can't let you do that starfox")
					fakeV2Actor.UploadApplicationPackageReturns(v2action.Warnings{"upload-warning-1", "upload-warning-2"}, expectedErr)

					done = make(chan bool)

					go func() {
						defer GinkgoRecover()

						Eventually(eventStream).Should(Receive(Equal(UploadingApplication)))
						Consistently(eventStream).ShouldNot(Receive())
						done <- true
					}()
				})

				AfterEach(func() {
					close(done)
				})

				It("returns the error and warnings", func() {
					Eventually(done).Should(Receive())
					Expect(executeErr).To(MatchError(expectedErr))
					Expect(warnings).To(ConsistOf("upload-warning-1", "upload-warning-2"))
				})
			})
		})

		Context("when the archive returns any access errors", func() {
			It("returns the error", func() {
				_, ok := executeErr.(*os.PathError)
				Expect(ok).To(BeTrue())
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"log"
)

type ChallengeItem struct {
	Alphabet string
	Maori    string
	Roman    string
	Ordinal  string
	Label    string
}

func main() {
	log.Println("INFO - Starting...")

	var set = map[string]ChallengeItem{
		"one": {
			Alphabet: "a",
			Maori:    "tahi",
			Roman:    "i",
			Ordinal:  "first",
		},

		"two": {
			Alphabet: "b",
			Maori:    "rua",
			Roman:    "ii",
			Ordinal:  "second",
		},
		"three": {
			Alphabet: "c",
			Maori:    "toru",
			Roman:    "iii",
			Ordinal:  "third",
		},
		"four": {
			Alphabet: "d",
			Maori:    "wha",
			Roman:    "iv",
			Ordinal:  "fourth",
		},
	}

	var keys = []string{"four", "two"}

	result, err := getOrderedSubset(set, keys)
	if err != nil {
		log.Println("ERROR -", err)
	} else {
		log.Println(result)
	}
}

func getOrderedSubset(set map[string]ChallengeItem, keys []string) ([]ChallengeItem, error) {
	var result = make([]ChallengeItem, 0)

	for _, key := range keys {
		if challengeItem, ok := set[key]; ok {
			challengeItem.Label = key
			result = append(result, challengeItem)
		} else {
			return result, errors.New("Invalid key")
		}
	}

	return result, nil
}
<commit_msg>Comments are lovely<commit_after>package main

import (
	"errors"
	"log"
)

/*
Represents a structure containing the values
specified in the challenge.
*/
type ChallengeItem struct {
	Alphabet string
	Maori    string
	Roman    string
	Ordinal  string
	Label    string
}

func main() {
	log.Println("INFO - Starting...")

	/*
	 * Make a map fo ChallengeItem items that looks like
	 * Mr. Cameron's example.
	 */
	var set = map[string]ChallengeItem{
		"one": {
			Alphabet: "a",
			Maori:    "tahi",
			Roman:    "i",
			Ordinal:  "first",
		},

		"two": {
			Alphabet: "b",
			Maori:    "rua",
			Roman:    "ii",
			Ordinal:  "second",
		},
		"three": {
			Alphabet: "c",
			Maori:    "toru",
			Roman:    "iii",
			Ordinal:  "third",
		},
		"four": {
			Alphabet: "d",
			Maori:    "wha",
			Roman:    "iv",
			Ordinal:  "fourth",
		},
	}

	/*
	 * Here are the keys we want to pull, and the
	 * order we want them in
	 */
	var keys = []string{"four", "two"}

	/*
	 * Call our function get our ordered ChallengeItem items
	 */
	result, err := getOrderedSubset(set, keys)
	if err != nil {
		log.Println("ERROR -", err)
	} else {
		log.Println(result)
	}
}

/*
Takes a set of ChallengeItem structs and an array of keys. This will return
an array of ChallengeItem structs for each key in the keys array. The resulting
ChallengeItem array will be ordered by the key order in the keys array.
*/
func getOrderedSubset(set map[string]ChallengeItem, keys []string) ([]ChallengeItem, error) {
	var result = make([]ChallengeItem, 0)

	for _, key := range keys {
		if challengeItem, ok := set[key]; ok {
			challengeItem.Label = key
			result = append(result, challengeItem)
		} else {
			return result, errors.New("Invalid key")
		}
	}

	return result, nil
}
<|endoftext|>


<commit_before>// Package limiter provides data structure to configure rate-limiter.
package limiter

import (
	"sync"
	"time"

	gocache "github.com/patrickmn/go-cache"
	"golang.org/x/time/rate"
)

// New is a constructor for Limiter.
func New(generalExpirableOptions *ExpirableOptions) *Limiter {
	lmt := &Limiter{}

	lmt.SetMessageContentType("text/plain; charset=utf-8").
		SetMessage("You have reached maximum request limit.").
		SetStatusCode(429).
		SetRejectFunc(nil).
		SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"}).
		SetHeaders(make(map[string][]string))

	if generalExpirableOptions != nil {
		lmt.tokenBucketOptions = generalExpirableOptions
	} else {
		lmt.tokenBucketOptions = &ExpirableOptions{}
	}

	// Default for ExpireJobInterval is every minute.
	if lmt.tokenBucketOptions.ExpireJobInterval <= 0 {
		lmt.tokenBucketOptions.ExpireJobInterval = time.Minute
	}

	// Default for DefaultExpirationTTL is 10 years.
	if lmt.tokenBucketOptions.DefaultExpirationTTL <= 0 {
		lmt.tokenBucketOptions.DefaultExpirationTTL = 87600 * time.Hour
	}

	lmt.tokenBuckets = gocache.New(
		lmt.tokenBucketOptions.DefaultExpirationTTL,
		lmt.tokenBucketOptions.ExpireJobInterval,
	)

	// TODO: for now use generalExpirableOptions for basicAuth expirable map.
	lmt.basicAuthUsers = gocache.New(
		lmt.tokenBucketOptions.DefaultExpirationTTL,
		lmt.tokenBucketOptions.ExpireJobInterval,
	)

	return lmt
}

// Limiter is a config struct to limit a particular request handler.
type Limiter struct {
	// Maximum number of requests to limit per duration.
	max int64

	// Duration of rate-limiter.
	ttl time.Duration

	// HTTP message when limit is reached.
	message string

	// Content-Type for Message
	messageContentType string

	// HTTP status code when limit is reached.
	statusCode int

	// A function to call when a request is rejected.
	rejectFunc func()

	// List of places to look up IP address.
	// Default is "RemoteAddr", "X-Forwarded-For", "X-Real-IP".
	// You can rearrange the order as you like.
	ipLookups []string

	// List of HTTP Methods to limit (GET, POST, PUT, etc.).
	// Empty means limit all methods.
	methods []string

	// List of basic auth usernames to limit.
	basicAuthUsers *gocache.Cache

	// Map of HTTP headers to limit.
	// Empty means skip headers checking.
	headers map[string][]string

	// Able to configure token bucket expirations.
	tokenBucketOptions *ExpirableOptions

	// Map of limiters with TTL
	tokenBuckets *gocache.Cache

	sync.RWMutex
}

// SetMax is thread-safe way of setting maximum number of requests to limit per duration.
func (l *Limiter) SetMax(max int64) *Limiter {
	l.Lock()
	l.max = max
	l.Unlock()

	return l
}

// GetMax is thread-safe way of getting maximum number of requests to limit per duration.
func (l *Limiter) GetMax() int64 {
	l.RLock()
	defer l.RUnlock()
	return l.max
}

// SetTTL is thread-safe way of setting maximum number of requests to limit per duration.
func (l *Limiter) SetTTL(ttl time.Duration) *Limiter {
	l.Lock()
	l.ttl = ttl
	l.Unlock()

	return l
}

// GetTTL is thread-safe way of getting maximum number of requests to limit per duration.
func (l *Limiter) GetTTL() time.Duration {
	l.RLock()
	defer l.RUnlock()
	return l.ttl
}

// SetMessage is thread-safe way of setting HTTP message when limit is reached.
func (l *Limiter) SetMessage(msg string) *Limiter {
	l.Lock()
	l.message = msg
	l.Unlock()

	return l
}

// GetMessage is thread-safe way of getting HTTP message when limit is reached.
func (l *Limiter) GetMessage() string {
	l.RLock()
	defer l.RUnlock()
	return l.message
}

// SetMessageContentType is thread-safe way of setting HTTP message Content-Type when limit is reached.
func (l *Limiter) SetMessageContentType(contentType string) *Limiter {
	l.Lock()
	l.messageContentType = contentType
	l.Unlock()

	return l
}

// GetMessageContentType is thread-safe way of getting HTTP message Content-Type when limit is reached.
func (l *Limiter) GetMessageContentType() string {
	l.RLock()
	defer l.RUnlock()
	return l.messageContentType
}

// SetStatusCode is thread-safe way of setting HTTP status code when limit is reached.
func (l *Limiter) SetStatusCode(statusCode int) *Limiter {
	l.Lock()
	l.statusCode = statusCode
	l.Unlock()

	return l
}

// GetStatusCode is thread-safe way of getting HTTP status code when limit is reached.
func (l *Limiter) GetStatusCode() int {
	l.RLock()
	defer l.RUnlock()
	return l.statusCode
}

// SetRejectFunc is thread-safe way of setting after-rejection function when limit is reached.
func (l *Limiter) SetRejectFunc(fn func()) *Limiter {
	l.Lock()
	l.rejectFunc = fn
	l.Unlock()

	return l
}

// ExecRejectFunc is thread-safe way of executing after-rejection function when limit is reached.
func (l *Limiter) ExecRejectFunc() {
	l.RLock()
	defer l.RUnlock()

	fn := l.rejectFunc
	if fn != nil {
		fn()
	}
}

// SetIPLookups is thread-safe way of setting list of places to look up IP address.
func (l *Limiter) SetIPLookups(ipLookups []string) *Limiter {
	l.Lock()
	l.ipLookups = ipLookups
	l.Unlock()

	return l
}

// GetIPLookups is thread-safe way of getting list of places to look up IP address.
func (l *Limiter) GetIPLookups() []string {
	l.RLock()
	defer l.RUnlock()
	return l.ipLookups
}

// SetMethods is thread-safe way of setting list of HTTP Methods to limit (GET, POST, PUT, etc.).
func (l *Limiter) SetMethods(methods []string) *Limiter {
	l.Lock()
	l.methods = methods
	l.Unlock()

	return l
}

// GetMethods is thread-safe way of getting list of HTTP Methods to limit (GET, POST, PUT, etc.).
func (l *Limiter) GetMethods() []string {
	l.RLock()
	defer l.RUnlock()
	return l.methods
}

// SetBasicAuthUsers is thread-safe way of setting list of basic auth usernames to limit.
func (l *Limiter) SetBasicAuthUsers(basicAuthUsers []string) *Limiter {
	for _, basicAuthUser := range basicAuthUsers {
		l.basicAuthUsers.Set(
			basicAuthUser,
			true,
			l.tokenBucketOptions.DefaultExpirationTTL,
		)
	}

	return l
}

// GetBasicAuthUsers is thread-safe way of getting list of basic auth usernames to limit.
func (l *Limiter) GetBasicAuthUsers() []string {
	asMap := l.basicAuthUsers.Items()

	var basicAuthUsers []string
	for basicAuthUser, _ := range asMap {
		basicAuthUsers = append(basicAuthUsers, basicAuthUser)
	}

	return basicAuthUsers
}

// RemoveBasicAuthUsers is thread-safe way of removing basic auth usernames from existing list.
func (l *Limiter) RemoveBasicAuthUsers(basicAuthUsers []string) *Limiter {
	for _, toBeRemoved := range basicAuthUsers {
		l.basicAuthUsers.Delete(toBeRemoved)
	}

	return l
}

// SetHeaders is thread-safe way of setting map of HTTP headers to limit.
func (l *Limiter) SetHeaders(headers map[string][]string) *Limiter {
	l.Lock()
	l.headers = headers
	l.Unlock()

	return l
}

// GetHeaders is thread-safe way of getting map of HTTP headers to limit.
func (l *Limiter) GetHeaders() map[string][]string {
	l.RLock()
	defer l.RUnlock()
	return l.headers
}

// SetHeader is thread-safe way of setting entries of 1 HTTP header.
func (l *Limiter) SetHeader(header string, entries []string) *Limiter {
	l.Lock()
	l.headers[header] = entries
	l.Unlock()

	return l
}

// GetHeader is thread-safe way of getting entries of 1 HTTP header.
func (l *Limiter) GetHeader(header string) []string {
	l.RLock()
	defer l.RUnlock()
	return l.headers[header]
}

// RemoveHeader is thread-safe way of removing entries of 1 HTTP header.
func (l *Limiter) RemoveHeader(header string) *Limiter {
	l.Lock()
	l.headers[header] = make([]string, 0)
	l.Unlock()

	return l
}

// AddHeaderEntries is thread-safe way of adding new entries to 1 HTTP header rule.
func (l *Limiter) AddHeaderEntries(header string, newEntries []string) *Limiter {
	l.Lock()
	defer l.Unlock()

	if len(l.headers[header]) == 0 {
		l.headers[header] = newEntries
		return l
	}

	for _, newEntry := range newEntries {
		alreadyExists := false
		for _, existing := range l.headers[header] {
			if existing == newEntry {
				alreadyExists = true
				break
			}
		}

		if !alreadyExists {
			l.headers[header] = append(l.headers[header], newEntry)
		}
	}

	return l
}

// RemoveHeaderEntries is thread-safe way of adding new entries to 1 HTTP header rule.
func (l *Limiter) RemoveHeaderEntries(header string, entriesForRemoval []string) *Limiter {
	newList := make([]string, 0)

	l.RLock()
	for _, existing := range l.headers[header] {
		found := false
		for _, toBeRemoves := range entriesForRemoval {
			if existing == toBeRemoves {
				found = true
				break
			}
		}

		if !found {
			newList = append(newList, existing)
		}
	}
	l.RUnlock()

	l.Lock()
	l.headers[header] = newList
	l.Unlock()

	return l
}

func (l *Limiter) isUsingTokenBucketsWithTTL() bool {
	if l.tokenBucketOptions == nil {
		return false
	}
	return l.tokenBucketOptions.DefaultExpirationTTL > 0
}

func (l *Limiter) limitReachedWithTokenBucketTTL(key string, tokenBucketTTL time.Duration) bool {
	lmtMax := l.GetMax()
	lmtTTL := l.GetTTL()

	l.Lock()
	defer l.Unlock()

	if _, found := l.tokenBuckets.Get(key); !found {
		l.tokenBuckets.Set(
			key,
			rate.NewLimiter(rate.Every(lmtTTL), int(lmtMax)),
			tokenBucketTTL,
		)
	}

	expiringMap, found := l.tokenBuckets.Get(key)
	if !found {
		return false
	}

	return !expiringMap.(*rate.Limiter).AllowN(time.Now(), 1)
}

// LimitReached returns a bool indicating if the Bucket identified by key ran out of tokens.
func (l *Limiter) LimitReached(key string) bool {
	return l.limitReachedWithTokenBucketTTL(key, gocache.DefaultExpiration)
}

// LimitReachedWithCustomTokenBucketTTL returns a bool indicating if the Bucket identified by key ran out of tokens.
// This public API allows user to define custom expiration TTL on the key.
func (l *Limiter) LimitReachedWithTokenBucketTTL(key string, tokenBucketTTL time.Duration) bool {
	return l.limitReachedWithTokenBucketTTL(key, tokenBucketTTL)
}
<commit_msg>Having one general expirable options is nice. We can re-use it for a lot of things.<commit_after>// Package limiter provides data structure to configure rate-limiter.
package limiter

import (
	"sync"
	"time"

	gocache "github.com/patrickmn/go-cache"
	"golang.org/x/time/rate"
)

// New is a constructor for Limiter.
func New(generalExpirableOptions *ExpirableOptions) *Limiter {
	lmt := &Limiter{}

	lmt.SetMessageContentType("text/plain; charset=utf-8").
		SetMessage("You have reached maximum request limit.").
		SetStatusCode(429).
		SetRejectFunc(nil).
		SetIPLookups([]string{"RemoteAddr", "X-Forwarded-For", "X-Real-IP"}).
		SetHeaders(make(map[string][]string))

	if generalExpirableOptions != nil {
		lmt.generalExpirableOptions = generalExpirableOptions
	} else {
		lmt.generalExpirableOptions = &ExpirableOptions{}
	}

	// Default for ExpireJobInterval is every minute.
	if lmt.generalExpirableOptions.ExpireJobInterval <= 0 {
		lmt.generalExpirableOptions.ExpireJobInterval = time.Minute
	}

	// Default for DefaultExpirationTTL is 10 years.
	if lmt.generalExpirableOptions.DefaultExpirationTTL <= 0 {
		lmt.generalExpirableOptions.DefaultExpirationTTL = 87600 * time.Hour
	}

	lmt.tokenBuckets = gocache.New(
		lmt.generalExpirableOptions.DefaultExpirationTTL,
		lmt.generalExpirableOptions.ExpireJobInterval,
	)

	// TODO: for now use generalExpirableOptions for basicAuth expirable map.
	lmt.basicAuthUsers = gocache.New(
		lmt.generalExpirableOptions.DefaultExpirationTTL,
		lmt.generalExpirableOptions.ExpireJobInterval,
	)

	return lmt
}

// Limiter is a config struct to limit a particular request handler.
type Limiter struct {
	// Maximum number of requests to limit per duration.
	max int64

	// Duration of rate-limiter.
	ttl time.Duration

	// HTTP message when limit is reached.
	message string

	// Content-Type for Message
	messageContentType string

	// HTTP status code when limit is reached.
	statusCode int

	// A function to call when a request is rejected.
	rejectFunc func()

	// List of places to look up IP address.
	// Default is "RemoteAddr", "X-Forwarded-For", "X-Real-IP".
	// You can rearrange the order as you like.
	ipLookups []string

	// List of HTTP Methods to limit (GET, POST, PUT, etc.).
	// Empty means limit all methods.
	methods []string

	// List of basic auth usernames to limit.
	basicAuthUsers *gocache.Cache

	// Map of HTTP headers to limit.
	// Empty means skip headers checking.
	headers map[string][]string

	// Able to configure token bucket expirations.
	generalExpirableOptions *ExpirableOptions

	// Map of limiters with TTL
	tokenBuckets *gocache.Cache

	sync.RWMutex
}

// SetMax is thread-safe way of setting maximum number of requests to limit per duration.
func (l *Limiter) SetMax(max int64) *Limiter {
	l.Lock()
	l.max = max
	l.Unlock()

	return l
}

// GetMax is thread-safe way of getting maximum number of requests to limit per duration.
func (l *Limiter) GetMax() int64 {
	l.RLock()
	defer l.RUnlock()
	return l.max
}

// SetTTL is thread-safe way of setting maximum number of requests to limit per duration.
func (l *Limiter) SetTTL(ttl time.Duration) *Limiter {
	l.Lock()
	l.ttl = ttl
	l.Unlock()

	return l
}

// GetTTL is thread-safe way of getting maximum number of requests to limit per duration.
func (l *Limiter) GetTTL() time.Duration {
	l.RLock()
	defer l.RUnlock()
	return l.ttl
}

// SetMessage is thread-safe way of setting HTTP message when limit is reached.
func (l *Limiter) SetMessage(msg string) *Limiter {
	l.Lock()
	l.message = msg
	l.Unlock()

	return l
}

// GetMessage is thread-safe way of getting HTTP message when limit is reached.
func (l *Limiter) GetMessage() string {
	l.RLock()
	defer l.RUnlock()
	return l.message
}

// SetMessageContentType is thread-safe way of setting HTTP message Content-Type when limit is reached.
func (l *Limiter) SetMessageContentType(contentType string) *Limiter {
	l.Lock()
	l.messageContentType = contentType
	l.Unlock()

	return l
}

// GetMessageContentType is thread-safe way of getting HTTP message Content-Type when limit is reached.
func (l *Limiter) GetMessageContentType() string {
	l.RLock()
	defer l.RUnlock()
	return l.messageContentType
}

// SetStatusCode is thread-safe way of setting HTTP status code when limit is reached.
func (l *Limiter) SetStatusCode(statusCode int) *Limiter {
	l.Lock()
	l.statusCode = statusCode
	l.Unlock()

	return l
}

// GetStatusCode is thread-safe way of getting HTTP status code when limit is reached.
func (l *Limiter) GetStatusCode() int {
	l.RLock()
	defer l.RUnlock()
	return l.statusCode
}

// SetRejectFunc is thread-safe way of setting after-rejection function when limit is reached.
func (l *Limiter) SetRejectFunc(fn func()) *Limiter {
	l.Lock()
	l.rejectFunc = fn
	l.Unlock()

	return l
}

// ExecRejectFunc is thread-safe way of executing after-rejection function when limit is reached.
func (l *Limiter) ExecRejectFunc() {
	l.RLock()
	defer l.RUnlock()

	fn := l.rejectFunc
	if fn != nil {
		fn()
	}
}

// SetIPLookups is thread-safe way of setting list of places to look up IP address.
func (l *Limiter) SetIPLookups(ipLookups []string) *Limiter {
	l.Lock()
	l.ipLookups = ipLookups
	l.Unlock()

	return l
}

// GetIPLookups is thread-safe way of getting list of places to look up IP address.
func (l *Limiter) GetIPLookups() []string {
	l.RLock()
	defer l.RUnlock()
	return l.ipLookups
}

// SetMethods is thread-safe way of setting list of HTTP Methods to limit (GET, POST, PUT, etc.).
func (l *Limiter) SetMethods(methods []string) *Limiter {
	l.Lock()
	l.methods = methods
	l.Unlock()

	return l
}

// GetMethods is thread-safe way of getting list of HTTP Methods to limit (GET, POST, PUT, etc.).
func (l *Limiter) GetMethods() []string {
	l.RLock()
	defer l.RUnlock()
	return l.methods
}

// SetBasicAuthUsers is thread-safe way of setting list of basic auth usernames to limit.
func (l *Limiter) SetBasicAuthUsers(basicAuthUsers []string) *Limiter {
	for _, basicAuthUser := range basicAuthUsers {
		l.basicAuthUsers.Set(
			basicAuthUser,
			true,
			l.generalExpirableOptions.DefaultExpirationTTL,
		)
	}

	return l
}

// GetBasicAuthUsers is thread-safe way of getting list of basic auth usernames to limit.
func (l *Limiter) GetBasicAuthUsers() []string {
	asMap := l.basicAuthUsers.Items()

	var basicAuthUsers []string
	for basicAuthUser, _ := range asMap {
		basicAuthUsers = append(basicAuthUsers, basicAuthUser)
	}

	return basicAuthUsers
}

// RemoveBasicAuthUsers is thread-safe way of removing basic auth usernames from existing list.
func (l *Limiter) RemoveBasicAuthUsers(basicAuthUsers []string) *Limiter {
	for _, toBeRemoved := range basicAuthUsers {
		l.basicAuthUsers.Delete(toBeRemoved)
	}

	return l
}

// SetHeaders is thread-safe way of setting map of HTTP headers to limit.
func (l *Limiter) SetHeaders(headers map[string][]string) *Limiter {
	l.Lock()
	l.headers = headers
	l.Unlock()

	return l
}

// GetHeaders is thread-safe way of getting map of HTTP headers to limit.
func (l *Limiter) GetHeaders() map[string][]string {
	l.RLock()
	defer l.RUnlock()
	return l.headers
}

// SetHeader is thread-safe way of setting entries of 1 HTTP header.
func (l *Limiter) SetHeader(header string, entries []string) *Limiter {
	l.Lock()
	l.headers[header] = entries
	l.Unlock()

	return l
}

// GetHeader is thread-safe way of getting entries of 1 HTTP header.
func (l *Limiter) GetHeader(header string) []string {
	l.RLock()
	defer l.RUnlock()
	return l.headers[header]
}

// RemoveHeader is thread-safe way of removing entries of 1 HTTP header.
func (l *Limiter) RemoveHeader(header string) *Limiter {
	l.Lock()
	l.headers[header] = make([]string, 0)
	l.Unlock()

	return l
}

// AddHeaderEntries is thread-safe way of adding new entries to 1 HTTP header rule.
func (l *Limiter) AddHeaderEntries(header string, newEntries []string) *Limiter {
	l.Lock()
	defer l.Unlock()

	if len(l.headers[header]) == 0 {
		l.headers[header] = newEntries
		return l
	}

	for _, newEntry := range newEntries {
		alreadyExists := false
		for _, existing := range l.headers[header] {
			if existing == newEntry {
				alreadyExists = true
				break
			}
		}

		if !alreadyExists {
			l.headers[header] = append(l.headers[header], newEntry)
		}
	}

	return l
}

// RemoveHeaderEntries is thread-safe way of adding new entries to 1 HTTP header rule.
func (l *Limiter) RemoveHeaderEntries(header string, entriesForRemoval []string) *Limiter {
	newList := make([]string, 0)

	l.RLock()
	for _, existing := range l.headers[header] {
		found := false
		for _, toBeRemoves := range entriesForRemoval {
			if existing == toBeRemoves {
				found = true
				break
			}
		}

		if !found {
			newList = append(newList, existing)
		}
	}
	l.RUnlock()

	l.Lock()
	l.headers[header] = newList
	l.Unlock()

	return l
}

func (l *Limiter) limitReachedWithTokenBucketTTL(key string, tokenBucketTTL time.Duration) bool {
	lmtMax := l.GetMax()
	lmtTTL := l.GetTTL()

	l.Lock()
	defer l.Unlock()

	if _, found := l.tokenBuckets.Get(key); !found {
		l.tokenBuckets.Set(
			key,
			rate.NewLimiter(rate.Every(lmtTTL), int(lmtMax)),
			tokenBucketTTL,
		)
	}

	expiringMap, found := l.tokenBuckets.Get(key)
	if !found {
		return false
	}

	return !expiringMap.(*rate.Limiter).AllowN(time.Now(), 1)
}

// LimitReached returns a bool indicating if the Bucket identified by key ran out of tokens.
func (l *Limiter) LimitReached(key string) bool {
	return l.limitReachedWithTokenBucketTTL(key, gocache.DefaultExpiration)
}

// LimitReachedWithCustomTokenBucketTTL returns a bool indicating if the Bucket identified by key ran out of tokens.
// This public API allows user to define custom expiration TTL on the key.
func (l *Limiter) LimitReachedWithTokenBucketTTL(key string, tokenBucketTTL time.Duration) bool {
	return l.limitReachedWithTokenBucketTTL(key, tokenBucketTTL)
}
<|endoftext|>


<commit_before>package smith

import (
	"encoding/json"
	"fmt"
)

// WatchEventHeader objects are streamed from the api server in response to a watch request.
type WatchEventHeader struct {
	// The type of the watch event; added, modified, deleted, or error.
	Type EventType `json:"type,omitempty" description:"the type of watch event; may be ADDED, MODIFIED, DELETED, or ERROR"`
}

type TemplateWatchEvent struct {
	Type EventType `json:"type"`

	// Object is:
	//  * If Type is Added or Modified: the new state of the object.
	//  * If Type is Deleted: the state of the object immediately before deletion.
	//  * If Type is Error: *api.Status is recommended; other types may make sense
	//    depending on context.
	Object *Template `json:"object"`
	Status *Status   `json:"-"`
}

func (twe *TemplateWatchEvent) UnmarshalJSON(data []byte) error {
	var weh WatchEventHeader
	if err := json.Unmarshal(data, &weh); err != nil {
		return err
	}
	twe.Type = weh.Type
	switch weh.Type {
	case Added, Modified, Deleted:
		var holder struct {
			Object Template `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		twe.Object = &holder.Object
	case Error:
		var holder struct {
			Object Status `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		twe.Status = &holder.Object
	default:
		return fmt.Errorf("unexpected event type: %s", weh.Type)
	}
	return nil
}

func (twe *TemplateWatchEvent) ResourceVersion() string {
	return twe.Object.ResourceVersion
}

// TprInstance describes some Third Party Resource instance.
// It contains only metadata about the object.
type TprInstance struct {
	TypeMeta `json:",inline"`

	// Standard object metadata
	ObjectMeta `json:"metadata,omitempty"`
}

// TprInstanceWatchEvent describes a watch event for some Third Party Resource instance.
type TprInstanceWatchEvent struct {
	Type EventType `json:"type"`

	// Object is:
	//  * If Type is Added or Modified: the new state of the object.
	//  * If Type is Deleted: the state of the object immediately before deletion.
	//  * If Type is Error: *api.Status is recommended; other types may make sense
	//    depending on context.
	Object *TprInstance `json:"object"`
	Status *Status      `json:"-"`
}

func (twe *TprInstanceWatchEvent) UnmarshalJSON(data []byte) error {
	var weh WatchEventHeader
	if err := json.Unmarshal(data, &weh); err != nil {
		return err
	}
	twe.Type = weh.Type
	switch weh.Type {
	case Added, Modified, Deleted:
		var holder struct {
			Object TprInstance `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		twe.Object = &holder.Object
	case Error:
		var holder struct {
			Object Status `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		twe.Status = &holder.Object
	default:
		return fmt.Errorf("unexpected event type: %s", weh.Type)
	}
	return nil
}

func (twe *TprInstanceWatchEvent) ResourceVersion() string {
	return twe.Object.ResourceVersion
}

// TprWatchEvent describes a watch event for Third Party Resource.
type TprWatchEvent struct {
	Type EventType `json:"type"`

	// Object is:
	//  * If Type is Added or Modified: the new state of the object.
	//  * If Type is Deleted: the state of the object immediately before deletion.
	//  * If Type is Error: *api.Status is recommended; other types may make sense
	//    depending on context.
	Object *ThirdPartyResource `json:"object"`
	Status *Status             `json:"-"`
}

func (twe *TprWatchEvent) UnmarshalJSON(data []byte) error {
	var weh WatchEventHeader
	if err := json.Unmarshal(data, &weh); err != nil {
		return err
	}
	twe.Type = weh.Type
	switch weh.Type {
	case Added, Modified, Deleted:
		var holder struct {
			Object ThirdPartyResource `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		twe.Object = &holder.Object
	case Error:
		var holder struct {
			Object Status `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		twe.Status = &holder.Object
	default:
		return fmt.Errorf("unexpected event type: %s", weh.Type)
	}
	return nil
}

func (twe *TprWatchEvent) ResourceVersion() string {
	return twe.Object.ResourceVersion
}
<commit_msg>DRY event unmarshaling<commit_after>package smith

import (
	"encoding/json"
	"fmt"
)

// WatchEventHeader objects are streamed from the api server in response to a watch request.
type WatchEventHeader struct {
	// The type of the watch event; added, modified, deleted, or error.
	Type EventType `json:"type,omitempty" description:"the type of watch event; may be ADDED, MODIFIED, DELETED, or ERROR"`
}

type TemplateWatchEvent struct {
	Type EventType `json:"type"`

	// Object is:
	//  * If Type is Added or Modified: the new state of the object.
	//  * If Type is Deleted: the state of the object immediately before deletion.
	//  * If Type is Error: *api.Status is recommended; other types may make sense
	//    depending on context.
	Object *Template `json:"object"`
	Status *Status   `json:"-"`
}

func (twe *TemplateWatchEvent) UnmarshalJSON(data []byte) error {
	var holder struct {
		Object Template `json:"object"`
	}
	if err := unmarshalEvent(data, &holder, &twe.Type, &twe.Status); err != nil {
		return err
	}
	twe.Object = &holder.Object
	return nil
}

func (twe *TemplateWatchEvent) ResourceVersion() string {
	return twe.Object.ResourceVersion
}

// TprInstance describes some Third Party Resource instance.
// It contains only metadata about the object.
type TprInstance struct {
	TypeMeta `json:",inline"`

	// Standard object metadata
	ObjectMeta `json:"metadata,omitempty"`
}

// TprInstanceWatchEvent describes a watch event for some Third Party Resource instance.
type TprInstanceWatchEvent struct {
	Type EventType `json:"type"`

	// Object is:
	//  * If Type is Added or Modified: the new state of the object.
	//  * If Type is Deleted: the state of the object immediately before deletion.
	//  * If Type is Error: *api.Status is recommended; other types may make sense
	//    depending on context.
	Object *TprInstance `json:"object"`
	Status *Status      `json:"-"`
}

func (twe *TprInstanceWatchEvent) UnmarshalJSON(data []byte) error {
	var holder struct {
		Object TprInstance `json:"object"`
	}
	if err := unmarshalEvent(data, &holder, &twe.Type, &twe.Status); err != nil {
		return err
	}
	twe.Object = &holder.Object
	return nil
}

func (twe *TprInstanceWatchEvent) ResourceVersion() string {
	return twe.Object.ResourceVersion
}

// TprWatchEvent describes a watch event for Third Party Resource.
type TprWatchEvent struct {
	Type EventType `json:"type"`

	// Object is:
	//  * If Type is Added or Modified: the new state of the object.
	//  * If Type is Deleted: the state of the object immediately before deletion.
	//  * If Type is Error: *api.Status is recommended; other types may make sense
	//    depending on context.
	Object *ThirdPartyResource `json:"object"`
	Status *Status             `json:"-"`
}

func (twe *TprWatchEvent) UnmarshalJSON(data []byte) error {
	var holder struct {
		Object ThirdPartyResource `json:"object"`
	}
	if err := unmarshalEvent(data, &holder, &twe.Type, &twe.Status); err != nil {
		return err
	}
	twe.Object = &holder.Object
	return nil
}

func (twe *TprWatchEvent) ResourceVersion() string {
	return twe.Object.ResourceVersion
}

func unmarshalEvent(data []byte, v interface{}, t *EventType, s **Status) error {
	var weh WatchEventHeader
	if err := json.Unmarshal(data, &weh); err != nil {
		return err
	}
	*t = weh.Type
	switch weh.Type {
	case Added, Modified, Deleted:
		return json.Unmarshal(data, v)
	case Error:
		var holder struct {
			Object Status `json:"object"`
		}
		if err := json.Unmarshal(data, &holder); err != nil {
			return err
		}
		*s = &holder.Object
	default:
		return fmt.Errorf("unexpected event type: %s", weh.Type)
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/luizbranco/eventsource"
)

var (
	port      = flag.String("port", "3001", "Eventsource port")
	amqpUrl   = flag.String("amqp-url", "amqp://guest:guest@localhost:5672/eventsource", "AMQP URL")
	statsdUrl = flag.String("statsd-url", "localhost:2003", "StatsD URL")
	prefix    = flag.String("statsd-prefix", "app.es_go", "StatsD Prefix")
	compress  = flag.Bool("compression", false, "Enable zlib compression of data")
)

func init() {
	flag.Parse()
}

func main() {

	server := &eventsource.Eventsource{
		ChanSub: eventsource.QueryStringChannels{Name: "channels"},
		Stats:   NewStats(*statsdUrl, *prefix),
	}
	server.Start()

	c := consumer{}
	messages, err := c.subscribe(*amqpUrl)
	if err != nil {
		log.Fatalf("[FATAL] AMQP %s", err)
	}

	go func() {
		for m := range messages {
			data, channels, err := parseMessage(m.Body)
			if err != nil {
				log.Printf("[WARN] json conversion failed %s", err)
			} else {
				e := eventsource.Event{
					Message:  data,
					Channels: channels,
					Compress: *compress,
				}
				server.Send(e)
			}
			m.Ack(false)
		}
		c.done <- nil
	}()

	http.Handle("/subscribe", server)
	log.Printf("[INFO] start port=%s amqp-url=%s statsd-url=%s statsd-prefix=%s"+
		" compression=%t ", *port, *amqpUrl, *statsdUrl, *prefix, *compress)
	err = http.ListenAndServe(":"+*port, nil)
	if err != nil {
		log.Fatalf("[FATAL] Server %s", err)
	}
}
<commit_msg>Use default statsd port<commit_after>package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/luizbranco/eventsource"
)

var (
	port      = flag.String("port", "3001", "Eventsource port")
	amqpUrl   = flag.String("amqp-url", "amqp://guest:guest@localhost:5672/eventsource", "AMQP URL")
	statsdUrl = flag.String("statsd-url", "localhost:8185", "StatsD URL")
	prefix    = flag.String("statsd-prefix", "app.es_go", "StatsD Prefix")
	compress  = flag.Bool("compression", false, "Enable zlib compression of data")
)

func init() {
	flag.Parse()
}

func main() {

	server := &eventsource.Eventsource{
		ChanSub: eventsource.QueryStringChannels{Name: "channels"},
		Stats:   NewStats(*statsdUrl, *prefix),
	}
	server.Start()

	c := consumer{}
	messages, err := c.subscribe(*amqpUrl)
	if err != nil {
		log.Fatalf("[FATAL] AMQP %s", err)
	}

	go func() {
		for m := range messages {
			data, channels, err := parseMessage(m.Body)
			if err != nil {
				log.Printf("[WARN] json conversion failed %s", err)
			} else {
				e := eventsource.Event{
					Message:  data,
					Channels: channels,
					Compress: *compress,
				}
				server.Send(e)
			}
			m.Ack(false)
		}
		c.done <- nil
	}()

	http.Handle("/subscribe", server)
	log.Printf("[INFO] start port=%s amqp-url=%s statsd-url=%s statsd-prefix=%s"+
		" compression=%t ", *port, *amqpUrl, *statsdUrl, *prefix, *compress)
	err = http.ListenAndServe(":"+*port, nil)
	if err != nil {
		log.Fatalf("[FATAL] Server %s", err)
	}
}
<|endoftext|>


<commit_before>package chip8

import (
	"testing"
)

func Test0xFx07(t *testing.T) {
	c := New([]byte{
		0xF0, 0x07,
	})
	c.delay = 0xFF

	c.Step()

	if c.v[0] != c.delay {
		t.Error("V0 not set to delay timer value")
	}
}

func Test0xFx0A(t *testing.T) {
	t.Skip("Not implemented")
}

func Test0xFx15(t *testing.T) {
	c := New([]byte{
		0xF0, 0x15,
	})
	c.v[0] = 0xFF

	c.Step()

	if c.delay != c.v[0] {
		t.Error("delay timer not set to V0")
	}
}

func Test0xFx18(t *testing.T) {
	c := New([]byte{
		0xF0, 0x18,
	})
	c.v[0] = 0xFF

	c.Step()

	if c.sound != c.v[0] {
		t.Error("sound timer not set to V0")
	}
}

func Test0xFx1E(t *testing.T) {
	c := New([]byte{
		0xF0, 0x1E,
	})
	c.v[0] = 0x0A
	c.i = 0x0A

	c.Step()

	if c.i != (0x0A + 0x0A) {
		t.Error("i not set to correct sum result")
	}
}

func Test0xFx29(t *testing.T) {
	t.Skip("Not implemented")
}

func Test0xFx33(t *testing.T) {
	var startMemoryAddress uint16 = 0x300
	var bcdTest byte = 251

	c := New([]byte{
		0xF0, 0x33,
	})

	c.i = startMemoryAddress
	c.v[0] = bcdTest // Vx

	c.Step()

	// Hundreds
	if c.memory[startMemoryAddress] != 2 {
		t.Error("BCD hundreds digit incorrect")
	}

	// Tens
	if c.memory[startMemoryAddress+1] != 5 {
		t.Error("BCD tens digit incorrect")
	}

	// Ones
	if c.memory[startMemoryAddress+2] != 1 {
		t.Error("BCD ones digit incorrect")
	}
}

var (
	memoryStartAddress uint16 = 0x300
	memoryValues              = []byte{
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
		0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
	}
)

func Test0xFx55(t *testing.T) {
	c := New([]byte{
		0xFB, 0x55,
	})

	// Setup register values
	for i := 0; i < len(memoryValues); i++ {
		c.v[i] = memoryValues[i]
	}
	c.i = memoryStartAddress

	c.Step()

	for i := 0; i < len(memoryValues); i++ {
		if c.memory[memoryStartAddress+uint16(i)] != c.v[i] {
			t.Errorf("memory at %#x does not contain correct value", memoryStartAddress+uint16(i))
		}
	}
}

func Test0xFx65(t *testing.T) {
	c := New([]byte{
		0xFB, 0x65,
	})

	// Setup register values
	for i := 0; i < len(memoryValues); i++ {
		c.memory[memoryStartAddress+uint16(i)] = memoryValues[i]
	}
	c.i = memoryStartAddress

	c.Step()

	for i := 0; i < len(memoryValues); i++ {
		if c.memory[memoryStartAddress+uint16(i)] != c.v[i] {
			t.Errorf("memory at %#x does not contain correct value", memoryStartAddress+uint16(i))
		}
	}
}
<commit_msg>Added unit test for 0xFx29<commit_after>package chip8

import (
	"math/rand"
	"testing"
)

func Test0xFx07(t *testing.T) {
	c := New([]byte{
		0xF0, 0x07,
	})
	c.delay = 0xFF

	c.Step()

	if c.v[0] != c.delay {
		t.Error("V0 not set to delay timer value")
	}
}

func Test0xFx0A(t *testing.T) {
	t.Skip("Not implemented")
}

func Test0xFx15(t *testing.T) {
	c := New([]byte{
		0xF0, 0x15,
	})
	c.v[0] = 0xFF

	c.Step()

	if c.delay != c.v[0] {
		t.Error("delay timer not set to V0")
	}
}

func Test0xFx18(t *testing.T) {
	c := New([]byte{
		0xF0, 0x18,
	})
	c.v[0] = 0xFF

	c.Step()

	if c.sound != c.v[0] {
		t.Error("sound timer not set to V0")
	}
}

func Test0xFx1E(t *testing.T) {
	c := New([]byte{
		0xF0, 0x1E,
	})
	c.v[0] = 0x0A
	c.i = 0x0A

	c.Step()

	if c.i != (0x0A + 0x0A) {
		t.Error("i not set to correct sum result")
	}
}

func Test0xFx29(t *testing.T) {
	hex := byte(rand.Int31n(0xF))

	c := New([]byte{
		0xF5, 0x29, // Hex character in V5
	})
	c.v[0x5] = hex

	c.Step()

	if c.i != uint16(fontStartAddress+(hex*5)) {
		t.Error("incorrect font memory address in I")
	}
}

func Test0xFx33(t *testing.T) {
	var startMemoryAddress uint16 = 0x300
	var bcdTest byte = 251

	c := New([]byte{
		0xF0, 0x33,
	})

	c.i = startMemoryAddress
	c.v[0] = bcdTest // Vx

	c.Step()

	// Hundreds
	if c.memory[startMemoryAddress] != 2 {
		t.Error("BCD hundreds digit incorrect")
	}

	// Tens
	if c.memory[startMemoryAddress+1] != 5 {
		t.Error("BCD tens digit incorrect")
	}

	// Ones
	if c.memory[startMemoryAddress+2] != 1 {
		t.Error("BCD ones digit incorrect")
	}
}

var (
	memoryStartAddress uint16 = 0x300
	memoryValues              = []byte{
		0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
		0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B,
	}
)

func Test0xFx55(t *testing.T) {
	c := New([]byte{
		0xFB, 0x55,
	})

	// Setup register values
	for i := 0; i < len(memoryValues); i++ {
		c.v[i] = memoryValues[i]
	}
	c.i = memoryStartAddress

	c.Step()

	for i := 0; i < len(memoryValues); i++ {
		if c.memory[memoryStartAddress+uint16(i)] != c.v[i] {
			t.Errorf("memory at %#x does not contain correct value", memoryStartAddress+uint16(i))
		}
	}
}

func Test0xFx65(t *testing.T) {
	c := New([]byte{
		0xFB, 0x65,
	})

	// Setup register values
	for i := 0; i < len(memoryValues); i++ {
		c.memory[memoryStartAddress+uint16(i)] = memoryValues[i]
	}
	c.i = memoryStartAddress

	c.Step()

	for i := 0; i < len(memoryValues); i++ {
		if c.memory[memoryStartAddress+uint16(i)] != c.v[i] {
			t.Errorf("memory at %#x does not contain correct value", memoryStartAddress+uint16(i))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package audio

import (
	"io"
	"runtime"

	"github.com/gopherjs/gopherjs/js"
)

type player struct {
	src          io.Reader
	sampleRate   int
	position     float64
	context      *js.Object
	bufferSource *js.Object
}

func startPlaying(src io.Reader, sampleRate int) (*player, error) {
	// Do nothing in node.js.
	if js.Global.Get("require") != js.Undefined {
		return nil, nil
	}

	class := js.Global.Get("AudioContext")
	if class == js.Undefined {
		class = js.Global.Get("webkitAudioContext")
	}
	if class == js.Undefined {
		panic("audio: audio couldn't be initialized")
	}
	p := &player{
		src:          src,
		sampleRate:   sampleRate,
		bufferSource: nil,
		context:      class.New(),
	}
	p.position = p.context.Get("currentTime").Float()
	if err := p.start(); err != nil {
		return nil, err
	}
	return p, nil
}

func toLR(data []byte) ([]int16, []int16) {
	l := make([]int16, len(data)/4)
	r := make([]int16, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = int16(data[4*i]) | int16(data[4*i+1])<<8
		r[i] = int16(data[4*i+2]) | int16(data[4*i+3])<<8
	}
	return l, r
}

func maxF(a, b float64) float64 {
	if a > b {
		return a
	}
	return b
}

func (p *player) proceed() error {
	const bufferSize = 4096
	c := p.context.Get("currentTime").Float()
	if c < p.position-bufferSize*0.5/float64(p.sampleRate) {
		return nil
	}
	if p.position < c {
		p.position = c
	}
	b := make([]byte, bufferSize)
	for 0 < len(b) {
		n, err := p.src.Read(b)
		if 0 < n {
			const channelNum = 2
			const bytesPerSample = channelNum * 16 / 8
			buf := p.context.Call("createBuffer", channelNum, n/bytesPerSample, p.sampleRate)
			l := buf.Call("getChannelData", 0)
			r := buf.Call("getChannelData", 1)
			il, ir := toLR(b[:n])
			const max = 1 << 15
			for i := 0; i < len(il); i++ {
				l.SetIndex(i, float64(il[i])/max)
				r.SetIndex(i, float64(ir[i])/max)
			}
			p.bufferSource = p.context.Call("createBufferSource")
			p.bufferSource.Set("buffer", buf)
			p.bufferSource.Call("connect", p.context.Get("destination"))
			p.bufferSource.Call("start", maxF(p.position, c))
			p.position += buf.Get("duration").Float()
			b = b[n:]
		}
		if err != nil {
			return err
		}
		runtime.Gosched()
	}
	return nil
}

func (p *player) start() error {
	// TODO: What if play is already called?
	go func() {
		defer p.close()
		for {
			err := p.proceed()
			if err == io.EOF {
				break
			}
			if err != nil {
				// TODO: Record the last error
				panic(err)
			}
			runtime.Gosched()
		}
	}()
	return nil
}

func (p *player) close() error {
	if p.bufferSource == nil {
		return nil
	}
	p.bufferSource.Call("stop")
	p.bufferSource.Call("disconnect")
	p.bufferSource = nil
	return nil
}
<commit_msg>audio: Change position from float64 to int64<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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.

// +build js

package audio

import (
	"io"
	"runtime"

	"github.com/gopherjs/gopherjs/js"
)

type player struct {
	src               io.Reader
	sampleRate        int
	positionInSamples int64
	context           *js.Object
	bufferSource      *js.Object
}

func startPlaying(src io.Reader, sampleRate int) (*player, error) {
	// Do nothing in node.js.
	if js.Global.Get("require") != js.Undefined {
		return nil, nil
	}

	class := js.Global.Get("AudioContext")
	if class == js.Undefined {
		class = js.Global.Get("webkitAudioContext")
	}
	if class == js.Undefined {
		panic("audio: audio couldn't be initialized")
	}
	p := &player{
		src:          src,
		sampleRate:   sampleRate,
		bufferSource: nil,
		context:      class.New(),
	}
	p.positionInSamples = int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
	if err := p.start(); err != nil {
		return nil, err
	}
	return p, nil
}

func toLR(data []byte) ([]int16, []int16) {
	l := make([]int16, len(data)/4)
	r := make([]int16, len(data)/4)
	for i := 0; i < len(data)/4; i++ {
		l[i] = int16(data[4*i]) | int16(data[4*i+1])<<8
		r[i] = int16(data[4*i+2]) | int16(data[4*i+3])<<8
	}
	return l, r
}

func max64(a, b int64) int64 {
	if a > b {
		return a
	}
	return b
}

func (p *player) proceed() error {
	const bufferSize = 4096
	c := int64(p.context.Get("currentTime").Float() * float64(p.sampleRate))
	if p.positionInSamples < c {
		p.positionInSamples = c
	}
	b := make([]byte, bufferSize)
	for 0 < len(b) {
		n, err := p.src.Read(b)
		if 0 < n {
			const channelNum = 2
			const bytesPerSample = channelNum * 16 / 8
			buf := p.context.Call("createBuffer", channelNum, n/bytesPerSample, p.sampleRate)
			l := buf.Call("getChannelData", 0)
			r := buf.Call("getChannelData", 1)
			il, ir := toLR(b[:n])
			const max = 1 << 15
			for i := 0; i < len(il); i++ {
				l.SetIndex(i, float64(il[i])/max)
				r.SetIndex(i, float64(ir[i])/max)
			}
			p.bufferSource = p.context.Call("createBufferSource")
			p.bufferSource.Set("buffer", buf)
			p.bufferSource.Call("connect", p.context.Get("destination"))
			p.bufferSource.Call("start", float64(max64(p.positionInSamples, c))/float64(p.sampleRate))
			p.positionInSamples += int64(len(il))
			b = b[n:]
		}
		if err != nil {
			return err
		}
		runtime.Gosched()
	}
	return nil
}

func (p *player) start() error {
	// TODO: What if play is already called?
	go func() {
		defer p.close()
		for {
			err := p.proceed()
			if err == io.EOF {
				break
			}
			if err != nil {
				// TODO: Record the last error
				panic(err)
			}
			runtime.Gosched()
		}
	}()
	return nil
}

func (p *player) close() error {
	if p.bufferSource == nil {
		return nil
	}
	p.bufferSource.Call("stop")
	p.bufferSource.Call("disconnect")
	p.bufferSource = nil
	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2018, The Decred developers
// Copyright (c) 2017, The dcrdata developers
// See LICENSE for details.

package explorer

import (
	"sync"
	"time"
)

const (
	wsWriteTimeout = 10 * time.Second
	wsReadTimeout  = 12 * time.Second
	pingInterval   = 8 * time.Second

	tickerSigReset int = iota
	tickerSigStop
	bufferSend

	bufferTickerInterval = 5
	newTxBufferSize      = 5
	clientSignalSize     = 5

	sigNewBlock hubSignal = iota
	sigMempoolUpdate
	sigPingAndUserCount
	sigNewTx
)

// WebSocketMessage represents the JSON object used to send and received typed
// messages to the web client.
type WebSocketMessage struct {
	EventId string `json:"event"`
	Message string `json:"message"`
}

// Event type field for an SSE event
var eventIDs = map[hubSignal]string{
	sigNewBlock:         "newblock",
	sigMempoolUpdate:    "mempool",
	sigPingAndUserCount: "ping",
	sigNewTx:            "newtx",
}

// WebsocketHub and its event loop manage all websocket client connections.
// WebsocketHub is responsible for closing all connections registered with it.
// If the event loop is running, calling (*WebsocketHub).Stop() will handle it.
type WebsocketHub struct {
	clients          map[*hubSpoke]*client
	Register         chan *clientHubSpoke
	Unregister       chan *hubSpoke
	HubRelay         chan hubSignal
	NewTxChan        chan *MempoolTx
	newTxBuffer      []*MempoolTx
	bufferMtx        *sync.Mutex
	bufferTickerChan chan int
	sendBufferChan   chan int
	quitWSHandler    chan struct{}
}

type client struct {
	sync.RWMutex
	newTxs []*MempoolTx
}

type hubSignal int
type hubSpoke chan hubSignal

// NewWebsocketHub creates a new WebsocketHub
func NewWebsocketHub() *WebsocketHub {
	return &WebsocketHub{
		clients:          make(map[*hubSpoke]*client),
		Register:         make(chan *clientHubSpoke),
		Unregister:       make(chan *hubSpoke),
		HubRelay:         make(chan hubSignal),
		NewTxChan:        make(chan *MempoolTx),
		newTxBuffer:      make([]*MempoolTx, 0, newTxBufferSize),
		bufferTickerChan: make(chan int, clientSignalSize),
		bufferMtx:        new(sync.Mutex),
		sendBufferChan:   make(chan int, clientSignalSize),
		quitWSHandler:    make(chan struct{}),
	}
}

type clientHubSpoke struct {
	cl *client
	c  *hubSpoke
}

// NumClients returns the number of clients connected to the websocket hub
func (wsh *WebsocketHub) NumClients() int {
	return len(wsh.clients)
}

// RegisterClient registers a websocket connection with the hub, and returns a
// pointer to the new client data object.
func (wsh *WebsocketHub) RegisterClient(c *hubSpoke) *client {
	cl := new(client)
	wsh.Register <- &clientHubSpoke{cl, c}
	return cl
}

// registerClient should only be called from the run loop
func (wsh *WebsocketHub) registerClient(ch *clientHubSpoke) {
	wsh.clients[ch.c] = ch.cl
	log.Debugf("Registered new websocket client (%d).", wsh.NumClients())
}

// UnregisterClient unregisters the input websocket connection via the main
// run() loop.  This call will block if the run() loop is not running.
func (wsh *WebsocketHub) UnregisterClient(c *hubSpoke) {
	wsh.Unregister <- c
}

// unregisterClient should only be called from the loop in run().
func (wsh *WebsocketHub) unregisterClient(c *hubSpoke) {
	if _, ok := wsh.clients[c]; !ok {
		// unknown client, do not close channel
		log.Warnf("unknown client")
		return
	}
	delete(wsh.clients, c)

	// Close the channel, but make sure the client didn't do it
	safeClose(*c)
}

// Periodically ping clients over websocket connection. Stop the ping loop by
// closing the returned channel.
func (wsh *WebsocketHub) pingClients() chan<- struct{} {
	stopPing := make(chan struct{})

	go func() {
		// start the client ping ticker
		ticker := time.NewTicker(pingInterval)
		defer ticker.Stop()

		for {
			select {
			case <-ticker.C:
				wsh.HubRelay <- sigPingAndUserCount
			case _, ok := <-stopPing:
				if !ok {
					log.Errorf("Do not send on stopPing channel, only close it.")
				}
				return
			}
		}
	}()

	return stopPing
}

func safeClose(cc hubSpoke) {
	select {
	case _, ok := <-cc:
		if !ok {
			log.Debug("Channel already closed!")
			return
		}
	default:
	}
	close(cc)
}

// Stop kills the run() loop and unregisteres all clients (connections).
func (wsh *WebsocketHub) Stop() {
	// end the run() loop, allowing in progress operations to complete
	wsh.quitWSHandler <- struct{}{}
}

func (wsh *WebsocketHub) run() {
	log.Info("Starting WebsocketHub run loop.")

	// start the buffer send ticker loop
	go wsh.periodicBufferSend()

	// start the client ping ticker
	stopPing := wsh.pingClients()
	defer close(stopPing)

	for {
	events:
		select {
		case hubSignal := <-wsh.HubRelay:
			var newtx *MempoolTx
			switch hubSignal {
			case sigNewBlock:
				log.Infof("Signaling new block to %d websocket clients.", len(wsh.clients))
			case sigPingAndUserCount:
				log.Tracef("Signaling ping/user count to %d websocket clients.", len(wsh.clients))
			case sigMempoolUpdate:
				log.Infof("Signaling mempool update to %d websocket clients.", len(wsh.clients))
			case sigNewTx:
				newtx = <-wsh.NewTxChan
				log.Tracef("Received new tx %s", newtx.Hash)
				wsh.MaybeSendTxns(newtx)
			default:
				log.Errorf("Unknown hub signal: %v", hubSignal)
				break events
			}
			for client := range wsh.clients {
				// Don't signal the client on new tx, another case handles that
				if hubSignal == sigNewTx {
					break
				}
				// signal or unregister the client
				select {
				case *client <- hubSignal:
				default:
					wsh.unregisterClient(client)
				}
			}
		case ch := <-wsh.Register:
			wsh.registerClient(ch)
		case c := <-wsh.Unregister:
			wsh.unregisterClient(c)
		case _, ok := <-wsh.quitWSHandler:
			if !ok {
				log.Error("close channel already closed. This should not happen.")
				return
			}
			close(wsh.quitWSHandler)

			// end the buffer interval send loop
			wsh.bufferTickerChan <- tickerSigStop

			// unregister all clients
			for client := range wsh.clients {
				wsh.unregisterClient(client)
			}
			return
		case <-wsh.sendBufferChan:
			wsh.bufferMtx.Lock()
			if len(wsh.newTxBuffer) == 0 {
				wsh.bufferMtx.Unlock()
				continue
			}
			txs := make([]*MempoolTx, len(wsh.newTxBuffer))
			copy(txs, wsh.newTxBuffer)
			wsh.newTxBuffer = make([]*MempoolTx, 0, newTxBufferSize)
			wsh.bufferMtx.Unlock()
			log.Debugf("Signaling %d new tx to %d clients", len(txs), len(wsh.clients))
			for signal, client := range wsh.clients {
				client.Lock()
				client.newTxs = txs
				client.Unlock()
				select {
				case *signal <- sigNewTx:
				default:
					wsh.unregisterClient(signal)
				}
			}
		}
	}
}

// MaybeSendTxns adds a mempool transaction to the client broadcast buffer. If
// the buffer is at capacity, a goroutine is launched to signal for the
// transactions to be sent to the clients.
func (wsh *WebsocketHub) MaybeSendTxns(tx *MempoolTx) {
	if wsh.addTxToBuffer(tx) {
		// This is called from the event loop, so these sends channel may not be
		// blocking.
		go func() {
			wsh.bufferTickerChan <- tickerSigReset
			wsh.sendBufferChan <- bufferSend
		}()
	}
}

// addTxToBuffer adds a tx to the buffer, then returns if the buffer is full
func (wsh *WebsocketHub) addTxToBuffer(tx *MempoolTx) bool {
	wsh.bufferMtx.Lock()
	defer wsh.bufferMtx.Unlock()

	wsh.newTxBuffer = append(wsh.newTxBuffer, tx)

	return len(wsh.newTxBuffer) >= newTxBufferSize
}

// periodicBufferSend initiates a buffer send every bufferTickerInterval seconds
func (wsh *WebsocketHub) periodicBufferSend() {
	ticker := time.NewTicker(bufferTickerInterval * time.Second)
	for {
		select {
		case <-ticker.C:
			wsh.sendBufferChan <- bufferSend
		case sig := <-wsh.bufferTickerChan:
			switch sig {
			case tickerSigReset:
				ticker.Stop()
				ticker = time.NewTicker(bufferTickerInterval * time.Second)
			case tickerSigStop:
				close(wsh.bufferTickerChan)
				return
			}
		}
	}
}
<commit_msg>explorer: limit logging about websocket updates to zero clients<commit_after>// Copyright (c) 2018, The Decred developers
// Copyright (c) 2017, The dcrdata developers
// See LICENSE for details.

package explorer

import (
	"sync"
	"time"
)

const (
	wsWriteTimeout = 10 * time.Second
	wsReadTimeout  = 12 * time.Second
	pingInterval   = 8 * time.Second

	tickerSigReset int = iota
	tickerSigStop
	bufferSend

	bufferTickerInterval = 5
	newTxBufferSize      = 5
	clientSignalSize     = 5

	sigNewBlock hubSignal = iota
	sigMempoolUpdate
	sigPingAndUserCount
	sigNewTx
)

// WebSocketMessage represents the JSON object used to send and received typed
// messages to the web client.
type WebSocketMessage struct {
	EventId string `json:"event"`
	Message string `json:"message"`
}

// Event type field for an SSE event
var eventIDs = map[hubSignal]string{
	sigNewBlock:         "newblock",
	sigMempoolUpdate:    "mempool",
	sigPingAndUserCount: "ping",
	sigNewTx:            "newtx",
}

// WebsocketHub and its event loop manage all websocket client connections.
// WebsocketHub is responsible for closing all connections registered with it.
// If the event loop is running, calling (*WebsocketHub).Stop() will handle it.
type WebsocketHub struct {
	clients          map[*hubSpoke]*client
	Register         chan *clientHubSpoke
	Unregister       chan *hubSpoke
	HubRelay         chan hubSignal
	NewTxChan        chan *MempoolTx
	newTxBuffer      []*MempoolTx
	bufferMtx        *sync.Mutex
	bufferTickerChan chan int
	sendBufferChan   chan int
	quitWSHandler    chan struct{}
}

type client struct {
	sync.RWMutex
	newTxs []*MempoolTx
}

type hubSignal int
type hubSpoke chan hubSignal

// NewWebsocketHub creates a new WebsocketHub
func NewWebsocketHub() *WebsocketHub {
	return &WebsocketHub{
		clients:          make(map[*hubSpoke]*client),
		Register:         make(chan *clientHubSpoke),
		Unregister:       make(chan *hubSpoke),
		HubRelay:         make(chan hubSignal),
		NewTxChan:        make(chan *MempoolTx),
		newTxBuffer:      make([]*MempoolTx, 0, newTxBufferSize),
		bufferTickerChan: make(chan int, clientSignalSize),
		bufferMtx:        new(sync.Mutex),
		sendBufferChan:   make(chan int, clientSignalSize),
		quitWSHandler:    make(chan struct{}),
	}
}

type clientHubSpoke struct {
	cl *client
	c  *hubSpoke
}

// NumClients returns the number of clients connected to the websocket hub
func (wsh *WebsocketHub) NumClients() int {
	return len(wsh.clients)
}

// RegisterClient registers a websocket connection with the hub, and returns a
// pointer to the new client data object.
func (wsh *WebsocketHub) RegisterClient(c *hubSpoke) *client {
	cl := new(client)
	wsh.Register <- &clientHubSpoke{cl, c}
	return cl
}

// registerClient should only be called from the run loop
func (wsh *WebsocketHub) registerClient(ch *clientHubSpoke) {
	wsh.clients[ch.c] = ch.cl
	log.Debugf("Registered new websocket client (%d).", wsh.NumClients())
}

// UnregisterClient unregisters the input websocket connection via the main
// run() loop.  This call will block if the run() loop is not running.
func (wsh *WebsocketHub) UnregisterClient(c *hubSpoke) {
	wsh.Unregister <- c
}

// unregisterClient should only be called from the loop in run().
func (wsh *WebsocketHub) unregisterClient(c *hubSpoke) {
	if _, ok := wsh.clients[c]; !ok {
		// unknown client, do not close channel
		log.Warnf("unknown client")
		return
	}
	delete(wsh.clients, c)

	// Close the channel, but make sure the client didn't do it
	safeClose(*c)
}

// Periodically ping clients over websocket connection. Stop the ping loop by
// closing the returned channel.
func (wsh *WebsocketHub) pingClients() chan<- struct{} {
	stopPing := make(chan struct{})

	go func() {
		// start the client ping ticker
		ticker := time.NewTicker(pingInterval)
		defer ticker.Stop()

		for {
			select {
			case <-ticker.C:
				wsh.HubRelay <- sigPingAndUserCount
			case _, ok := <-stopPing:
				if !ok {
					log.Errorf("Do not send on stopPing channel, only close it.")
				}
				return
			}
		}
	}()

	return stopPing
}

func safeClose(cc hubSpoke) {
	select {
	case _, ok := <-cc:
		if !ok {
			log.Debug("Channel already closed!")
			return
		}
	default:
	}
	close(cc)
}

// Stop kills the run() loop and unregisteres all clients (connections).
func (wsh *WebsocketHub) Stop() {
	// end the run() loop, allowing in progress operations to complete
	wsh.quitWSHandler <- struct{}{}
}

func (wsh *WebsocketHub) run() {
	log.Info("Starting WebsocketHub run loop.")

	// start the buffer send ticker loop
	go wsh.periodicBufferSend()

	// start the client ping ticker
	stopPing := wsh.pingClients()
	defer close(stopPing)

	for {
	events:
		select {
		case hubSignal := <-wsh.HubRelay:
			var newtx *MempoolTx
			switch hubSignal {
			case sigNewBlock:
				log.Infof("Signaling new block to %d websocket clients.", len(wsh.clients))
			case sigPingAndUserCount:
				log.Tracef("Signaling ping/user count to %d websocket clients.", len(wsh.clients))
			case sigMempoolUpdate:
				if len(wsh.clients) > 0 {
					log.Infof("Signaling mempool update to %d websocket clients.", len(wsh.clients))
				}
			case sigNewTx:
				newtx = <-wsh.NewTxChan
				log.Tracef("Received new tx %s", newtx.Hash)
				wsh.MaybeSendTxns(newtx)
			default:
				log.Errorf("Unknown hub signal: %v", hubSignal)
				break events
			}
			for client := range wsh.clients {
				// Don't signal the client on new tx, another case handles that
				if hubSignal == sigNewTx {
					break
				}
				// signal or unregister the client
				select {
				case *client <- hubSignal:
				default:
					wsh.unregisterClient(client)
				}
			}
		case ch := <-wsh.Register:
			wsh.registerClient(ch)
		case c := <-wsh.Unregister:
			wsh.unregisterClient(c)
		case _, ok := <-wsh.quitWSHandler:
			if !ok {
				log.Error("close channel already closed. This should not happen.")
				return
			}
			close(wsh.quitWSHandler)

			// end the buffer interval send loop
			wsh.bufferTickerChan <- tickerSigStop

			// unregister all clients
			for client := range wsh.clients {
				wsh.unregisterClient(client)
			}
			return
		case <-wsh.sendBufferChan:
			wsh.bufferMtx.Lock()
			if len(wsh.newTxBuffer) == 0 {
				wsh.bufferMtx.Unlock()
				continue
			}
			txs := make([]*MempoolTx, len(wsh.newTxBuffer))
			copy(txs, wsh.newTxBuffer)
			wsh.newTxBuffer = make([]*MempoolTx, 0, newTxBufferSize)
			wsh.bufferMtx.Unlock()
			if len(wsh.clients) > 0 {
				log.Debugf("Signaling %d new tx to %d clients", len(txs), len(wsh.clients))
			}
			for signal, client := range wsh.clients {
				client.Lock()
				client.newTxs = txs
				client.Unlock()
				select {
				case *signal <- sigNewTx:
				default:
					wsh.unregisterClient(signal)
				}
			}
		}
	}
}

// MaybeSendTxns adds a mempool transaction to the client broadcast buffer. If
// the buffer is at capacity, a goroutine is launched to signal for the
// transactions to be sent to the clients.
func (wsh *WebsocketHub) MaybeSendTxns(tx *MempoolTx) {
	if wsh.addTxToBuffer(tx) {
		// This is called from the event loop, so these sends channel may not be
		// blocking.
		go func() {
			wsh.bufferTickerChan <- tickerSigReset
			wsh.sendBufferChan <- bufferSend
		}()
	}
}

// addTxToBuffer adds a tx to the buffer, then returns if the buffer is full
func (wsh *WebsocketHub) addTxToBuffer(tx *MempoolTx) bool {
	wsh.bufferMtx.Lock()
	defer wsh.bufferMtx.Unlock()

	wsh.newTxBuffer = append(wsh.newTxBuffer, tx)

	return len(wsh.newTxBuffer) >= newTxBufferSize
}

// periodicBufferSend initiates a buffer send every bufferTickerInterval seconds
func (wsh *WebsocketHub) periodicBufferSend() {
	ticker := time.NewTicker(bufferTickerInterval * time.Second)
	for {
		select {
		case <-ticker.C:
			wsh.sendBufferChan <- bufferSend
		case sig := <-wsh.bufferTickerChan:
			switch sig {
			case tickerSigReset:
				ticker.Stop()
				ticker = time.NewTicker(bufferTickerInterval * time.Second)
			case tickerSigStop:
				close(wsh.bufferTickerChan)
				return
			}
		}
	}
}
<|endoftext|>


<commit_before>package lfshttp

import (
	"fmt"
	"net/http"

	"github.com/google/slothfs/cookie"
)

func isCookieJarEnabledForHost(c *Client, host string) bool {
	_, cookieFileOk := c.uc.Get("http", fmt.Sprintf("https://%v", host), "cookieFile")

	return cookieFileOk
}

func getCookieJarForHost(c *Client, host string) (http.CookieJar, error) {
cookieFile, _ := c.uc.Get("http", fmt.Sprintf("https://%v", host), "cookieFile")

	return cookie.NewJar(cookieFile)
}
<commit_msg>Fix indentation<commit_after>package lfshttp

import (
	"fmt"
	"net/http"

	"github.com/google/slothfs/cookie"
)

func isCookieJarEnabledForHost(c *Client, host string) bool {
	_, cookieFileOk := c.uc.Get("http", fmt.Sprintf("https://%v", host), "cookieFile")

	return cookieFileOk
}

func getCookieJarForHost(c *Client, host string) (http.CookieJar, error) {
	cookieFile, _ := c.uc.Get("http", fmt.Sprintf("https://%v", host), "cookieFile")

	return cookie.NewJar(cookieFile)
}
<|endoftext|>