<commit_before>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package index

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/utils/hashing"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/vms/components/avax"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

const DatabaseOpErrorExitCode int = 5

var (
	idxKey         = []byte("idx")
	idxCompleteKey = []byte("complete")
)

// AddressTxsIndexer maintains information about which transactions changed
// the balances of which addresses. This includes both transactions that
// increase and decrease an address's balance.
// A transaction is said to change an address's balance if either is true:
// 1) A UTXO that the transaction consumes was at least partially owned by the address.
// 2) A UTXO that the transaction produces is at least partially owned by the address.
type AddressTxsIndexer interface {
	// Add is called during [txID]'s SemanticVerify.
	// [inputUTXOIDs] are the IDs of UTXOs [txID] consumes.
	// [outputUTXOs] are the UTXOs [txID] creates.
	// [getUTXOF] can be used to look up UTXOs by ID.
	// If the error is non-nil, do not persist [txID] to disk as accepted in the VM,
	// and shut down this chain.
	Add(
		txID ids.ID,
		inputUTXOIDs []*avax.UTXOID,
		outputUTXOs []*avax.UTXO,
		getUTXOF func(utxoID *avax.UTXOID) (*avax.UTXO, error),
	) error

	// Accept is called when [txID] is accepted.
	// Persists data about [txID] and what balances it changed.
	// If the error is non-nil, do not persist [txID] to disk as accepted in the VM,
	// and shut down this chain.
	Accept(txID ids.ID) error

	// Clear is called when [txID] is rejected or fails verification.
	// Clears unwritten state about the tx.
	Clear(ids.ID)

	// Read returns the IDs of transactions that changed [address]'s balance of [assetID].
	// The returned transactions are in order of increasing acceptance time.
	// The length of the returned slice <= [pageSize].
	// [cursor] is the offset to start reading from.
	Read(address ids.ShortID, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error)
}

// indexer implements AddressTxsIndexer
type indexer struct {
	log     logging.Logger
	metrics metrics
	db      database.Database
	// txID -> Address -> AssetID --> exists if the address's balance
	// of the asset is changed by processing tx [txID]
	balanceChanges map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}
}

// NewIndexer Returns a new AddressTxsIndexer.
// The returned indexer ignores UTXOs that are not type secp256k1fx.TransferOutput.
func NewIndexer(
	db database.Database,
	log logging.Logger,
	metricsNamespace string,
	metricsRegisterer prometheus.Registerer,
	allowIncompleteIndices bool,
) (AddressTxsIndexer, error) {
	i := &indexer{
		balanceChanges: make(map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}),
		db:             db,
		log:            log,
	}
	// initialize the indexer
	if err := i.init(allowIncompleteIndices); err != nil {
		return nil, err
	}
	// initialize the metrics
	if err := i.metrics.initialize(metricsNamespace, metricsRegisterer); err != nil {
		return nil, err
	}
	return i, nil
}

// add marks that [txID] changes the balance of [assetID] for [addrs]
// This data is either written in Accept() or cleared in Clear()
func (i *indexer) add(txID, assetID ids.ID, addrs [][]byte) error {
	for _, addressBytes := range addrs {
		address, err := ids.ToShortID(addressBytes)
		if err != nil {
			// should never happen
			return err
		}
		if _, exists := i.balanceChanges[txID]; !exists {
			i.balanceChanges[txID] = make(map[ids.ShortID]map[ids.ID]struct{})
		}
		if _, exists := i.balanceChanges[txID][address]; !exists {
			i.balanceChanges[txID][address] = make(map[ids.ID]struct{})
		}
		i.balanceChanges[txID][address][assetID] = struct{}{}
	}
	return nil
}

// See AddressTxsIndexer
func (i *indexer) Add(
	txID ids.ID,
	inputUTXOIDs []*avax.UTXOID,
	outputUTXOs []*avax.UTXO,
	getUTXOF func(utxoID *avax.UTXOID) (*avax.UTXO, error),
) error {
	utxos := outputUTXOs
	for _, utxoID := range inputUTXOIDs {
		utxo, err := getUTXOF(utxoID)
		if err != nil { // should never happen
			return fmt.Errorf("error finding UTXO %s: %s", utxoID, err)
		}
		utxos = append(utxos, utxo)
	}
	for _, utxo := range utxos {
		out, ok := utxo.Out.(avax.Addressable)
		if !ok {
			i.log.Verbo("skipping UTXO %s for indexing", utxo.InputID())
			continue
		}
		if err := i.add(txID, utxo.AssetID(), out.Addresses()); err != nil {
			return fmt.Errorf("error adding to index: %s", err)
		}
	}
	return nil
}

// Accept persists which balances [txID] changed.
// Associates all UTXOs in [i.balanceChanges] with transaction [txID].
// The database structure is:
// [address]
// |  [assetID]
// |  |
// |  | "idx" => 2 		Running transaction index key, represents the next index
// |  | "0"   => txID1
// |  | "1"   => txID1
// See AddressTxsIndexer
func (i *indexer) Accept(txID ids.ID) error {
	for address, assetIDs := range i.balanceChanges[txID] {
		addressPrefixDB := prefixdb.New(address[:], i.db)
		for assetID := range assetIDs {
			assetPrefixDB := prefixdb.New(assetID[:], addressPrefixDB)

			var idx uint64
			idxBytes, err := assetPrefixDB.Get(idxKey)
			switch {
			case err != nil && err != database.ErrNotFound:
				// Unexpected error
				return fmt.Errorf("unexpected error when indexing txID %s: %s", txID, err)
			case err == database.ErrNotFound:
				// idx not found; this must be the first entry.
				idx = 0
				idxBytes = make([]byte, wrappers.LongLen)
				binary.BigEndian.PutUint64(idxBytes, idx)
			default:
				// index is found, parse stored [idxBytes]
				idx = binary.BigEndian.Uint64(idxBytes)
			}

			// write the [txID] at the index
			i.log.Verbo("writing address/assetID/index/txID %s/%s/%d/%s", address, assetID, idx, txID)
			if err := assetPrefixDB.Put(idxBytes, txID[:]); err != nil {
				return fmt.Errorf("failed to write txID while indexing %s: %s", txID, err)
			}

			// increment and store the index for next use
			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)

			if err := assetPrefixDB.Put(idxKey, idxBytes); err != nil {
				return fmt.Errorf("failed to write index txID while indexing %s: %s", txID, err)
			}
		}
	}
	// delete already written [txID] from the map
	delete(i.balanceChanges, txID)
	i.metrics.numTxsIndexed.Observe(1)
	return nil
}

// Read returns IDs of transactions that changed [address]'s balance of [assetID],
// starting at [cursor], in order of transaction acceptance. e.g. if [cursor] == 1, does
// not return the first transaction that changed the balance. (This is for for pagination.)
// Returns at most [pageSize] elements.
// See AddressTxsIndexer
func (i *indexer) Read(address ids.ShortID, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error) {
	// setup prefix DBs
	addressTxDB := prefixdb.New(address[:], i.db)
	assetPrefixDB := prefixdb.New(assetID[:], addressTxDB)

	// get cursor in bytes
	cursorBytes := make([]byte, wrappers.LongLen)
	binary.BigEndian.PutUint64(cursorBytes, cursor)

	// start reading from the cursor bytes, numeric keys maintain the order (see Accept)
	iter := assetPrefixDB.NewIteratorWithStart(cursorBytes)
	defer iter.Release()

	var txIDs []ids.ID
	for uint64(len(txIDs)) < pageSize && iter.Next() {
		if bytes.Equal(idxKey, iter.Key()) {
			// This key has the next index to use, not a tx ID
			continue
		}

		// get the value, make sure its in the right format
		txIDBytes := iter.Value()
		if len(txIDBytes) != hashing.HashLen {
			return nil, fmt.Errorf("invalid tx ID %s", txIDBytes)
		}

		// get the  txID and append to our list
		var txID ids.ID
		copy(txID[:], txIDBytes)
		txIDs = append(txIDs, txID)
	}
	return txIDs, nil
}

// Clear clears data about which balances [txID] changed.
func (i *indexer) Clear(txID ids.ID) {
	i.balanceChanges[txID] = make(map[ids.ShortID]map[ids.ID]struct{})
}

// Init initialises indexing, returning error if the state is invalid
func (i *indexer) init(allowIncomplete bool) error {
	return checkIndexStatus(i.db, true, allowIncomplete)
}

// checkIndexStatus checks the indexing status in the database, returning error if the state
// with respect to provided parameters is invalid
func checkIndexStatus(db database.KeyValueReaderWriter, enableIndexing, allowIncomplete bool) error {
	// verify whether the index is complete.
	idxComplete, err := database.GetBool(db, idxCompleteKey)
	if err == database.ErrNotFound {
		// We've not run before. Mark whether indexing is enabled this run.
		return database.PutBool(db, idxCompleteKey, enableIndexing)
	} else if err != nil {
		return err
	}

	if !idxComplete { // In a previous run, we did not index so it's incomplete.
		if enableIndexing && !allowIncomplete {
			// indexing was disabled before but now we want to index.
			return errors.New("running would create incomplete index. Allow incomplete indices or re-sync from genesis with indexing enabled")
		}
		// either indexing is disabled, or incomplete indices are ok, so we don't care that index is incomplete
		return nil
	}

	// the index is complete
	if !enableIndexing { // indexing is disabled this run
		if !allowIncomplete {
			return errors.New("running would create incomplete index. Allow incomplete indices or enable indexing")
		}
		// running without indexing makes it incomplete
		return database.PutBool(db, idxCompleteKey, false)
	}
	return nil
}

type noIndexer struct{}

func NewNoIndexer(db database.Database, allowIncomplete bool) (AddressTxsIndexer, error) {
	if err := checkIndexStatus(db, false, allowIncomplete); err != nil {
		return nil, err
	}
	return &noIndexer{}, nil
}

func (i *noIndexer) Add(ids.ID, []*avax.UTXOID, []*avax.UTXO, func(utxoID *avax.UTXOID) (*avax.UTXO, error)) error {
	return nil
}

func (i *noIndexer) Accept(ids.ID) error {
	return nil
}

func (i *noIndexer) Clear(ids.ID) {}

func (i *noIndexer) Read(ids.ShortID, ids.ID, uint64, uint64) ([]ids.ID, error) {
	return nil, nil
}
<commit_msg>remove redundant constant<commit_after>// (c) 2021, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

package index

import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"

	"github.com/ava-labs/avalanchego/utils/hashing"
	"github.com/prometheus/client_golang/prometheus"

	"github.com/ava-labs/avalanchego/utils/logging"
	"github.com/ava-labs/avalanchego/vms/components/avax"

	"github.com/ava-labs/avalanchego/database"
	"github.com/ava-labs/avalanchego/database/prefixdb"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/utils/wrappers"
)

var (
	idxKey         = []byte("idx")
	idxCompleteKey = []byte("complete")
)

// AddressTxsIndexer maintains information about which transactions changed
// the balances of which addresses. This includes both transactions that
// increase and decrease an address's balance.
// A transaction is said to change an address's balance if either is true:
// 1) A UTXO that the transaction consumes was at least partially owned by the address.
// 2) A UTXO that the transaction produces is at least partially owned by the address.
type AddressTxsIndexer interface {
	// Add is called during [txID]'s SemanticVerify.
	// [inputUTXOIDs] are the IDs of UTXOs [txID] consumes.
	// [outputUTXOs] are the UTXOs [txID] creates.
	// [getUTXOF] can be used to look up UTXOs by ID.
	// If the error is non-nil, do not persist [txID] to disk as accepted in the VM,
	// and shut down this chain.
	Add(
		txID ids.ID,
		inputUTXOIDs []*avax.UTXOID,
		outputUTXOs []*avax.UTXO,
		getUTXOF func(utxoID *avax.UTXOID) (*avax.UTXO, error),
	) error

	// Accept is called when [txID] is accepted.
	// Persists data about [txID] and what balances it changed.
	// If the error is non-nil, do not persist [txID] to disk as accepted in the VM,
	// and shut down this chain.
	Accept(txID ids.ID) error

	// Clear is called when [txID] is rejected or fails verification.
	// Clears unwritten state about the tx.
	Clear(ids.ID)

	// Read returns the IDs of transactions that changed [address]'s balance of [assetID].
	// The returned transactions are in order of increasing acceptance time.
	// The length of the returned slice <= [pageSize].
	// [cursor] is the offset to start reading from.
	Read(address ids.ShortID, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error)
}

// indexer implements AddressTxsIndexer
type indexer struct {
	log     logging.Logger
	metrics metrics
	db      database.Database
	// txID -> Address -> AssetID --> exists if the address's balance
	// of the asset is changed by processing tx [txID]
	balanceChanges map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}
}

// NewIndexer Returns a new AddressTxsIndexer.
// The returned indexer ignores UTXOs that are not type secp256k1fx.TransferOutput.
func NewIndexer(
	db database.Database,
	log logging.Logger,
	metricsNamespace string,
	metricsRegisterer prometheus.Registerer,
	allowIncompleteIndices bool,
) (AddressTxsIndexer, error) {
	i := &indexer{
		balanceChanges: make(map[ids.ID]map[ids.ShortID]map[ids.ID]struct{}),
		db:             db,
		log:            log,
	}
	// initialize the indexer
	if err := i.init(allowIncompleteIndices); err != nil {
		return nil, err
	}
	// initialize the metrics
	if err := i.metrics.initialize(metricsNamespace, metricsRegisterer); err != nil {
		return nil, err
	}
	return i, nil
}

// add marks that [txID] changes the balance of [assetID] for [addrs]
// This data is either written in Accept() or cleared in Clear()
func (i *indexer) add(txID, assetID ids.ID, addrs [][]byte) error {
	for _, addressBytes := range addrs {
		address, err := ids.ToShortID(addressBytes)
		if err != nil {
			// should never happen
			return err
		}
		if _, exists := i.balanceChanges[txID]; !exists {
			i.balanceChanges[txID] = make(map[ids.ShortID]map[ids.ID]struct{})
		}
		if _, exists := i.balanceChanges[txID][address]; !exists {
			i.balanceChanges[txID][address] = make(map[ids.ID]struct{})
		}
		i.balanceChanges[txID][address][assetID] = struct{}{}
	}
	return nil
}

// See AddressTxsIndexer
func (i *indexer) Add(
	txID ids.ID,
	inputUTXOIDs []*avax.UTXOID,
	outputUTXOs []*avax.UTXO,
	getUTXOF func(utxoID *avax.UTXOID) (*avax.UTXO, error),
) error {
	utxos := outputUTXOs
	for _, utxoID := range inputUTXOIDs {
		utxo, err := getUTXOF(utxoID)
		if err != nil { // should never happen
			return fmt.Errorf("error finding UTXO %s: %s", utxoID, err)
		}
		utxos = append(utxos, utxo)
	}
	for _, utxo := range utxos {
		out, ok := utxo.Out.(avax.Addressable)
		if !ok {
			i.log.Verbo("skipping UTXO %s for indexing", utxo.InputID())
			continue
		}
		if err := i.add(txID, utxo.AssetID(), out.Addresses()); err != nil {
			return fmt.Errorf("error adding to index: %s", err)
		}
	}
	return nil
}

// Accept persists which balances [txID] changed.
// Associates all UTXOs in [i.balanceChanges] with transaction [txID].
// The database structure is:
// [address]
// |  [assetID]
// |  |
// |  | "idx" => 2 		Running transaction index key, represents the next index
// |  | "0"   => txID1
// |  | "1"   => txID1
// See AddressTxsIndexer
func (i *indexer) Accept(txID ids.ID) error {
	for address, assetIDs := range i.balanceChanges[txID] {
		addressPrefixDB := prefixdb.New(address[:], i.db)
		for assetID := range assetIDs {
			assetPrefixDB := prefixdb.New(assetID[:], addressPrefixDB)

			var idx uint64
			idxBytes, err := assetPrefixDB.Get(idxKey)
			switch {
			case err != nil && err != database.ErrNotFound:
				// Unexpected error
				return fmt.Errorf("unexpected error when indexing txID %s: %s", txID, err)
			case err == database.ErrNotFound:
				// idx not found; this must be the first entry.
				idx = 0
				idxBytes = make([]byte, wrappers.LongLen)
				binary.BigEndian.PutUint64(idxBytes, idx)
			default:
				// index is found, parse stored [idxBytes]
				idx = binary.BigEndian.Uint64(idxBytes)
			}

			// write the [txID] at the index
			i.log.Verbo("writing address/assetID/index/txID %s/%s/%d/%s", address, assetID, idx, txID)
			if err := assetPrefixDB.Put(idxBytes, txID[:]); err != nil {
				return fmt.Errorf("failed to write txID while indexing %s: %s", txID, err)
			}

			// increment and store the index for next use
			idx++
			binary.BigEndian.PutUint64(idxBytes, idx)

			if err := assetPrefixDB.Put(idxKey, idxBytes); err != nil {
				return fmt.Errorf("failed to write index txID while indexing %s: %s", txID, err)
			}
		}
	}
	// delete already written [txID] from the map
	delete(i.balanceChanges, txID)
	i.metrics.numTxsIndexed.Observe(1)
	return nil
}

// Read returns IDs of transactions that changed [address]'s balance of [assetID],
// starting at [cursor], in order of transaction acceptance. e.g. if [cursor] == 1, does
// not return the first transaction that changed the balance. (This is for for pagination.)
// Returns at most [pageSize] elements.
// See AddressTxsIndexer
func (i *indexer) Read(address ids.ShortID, assetID ids.ID, cursor, pageSize uint64) ([]ids.ID, error) {
	// setup prefix DBs
	addressTxDB := prefixdb.New(address[:], i.db)
	assetPrefixDB := prefixdb.New(assetID[:], addressTxDB)

	// get cursor in bytes
	cursorBytes := make([]byte, wrappers.LongLen)
	binary.BigEndian.PutUint64(cursorBytes, cursor)

	// start reading from the cursor bytes, numeric keys maintain the order (see Accept)
	iter := assetPrefixDB.NewIteratorWithStart(cursorBytes)
	defer iter.Release()

	var txIDs []ids.ID
	for uint64(len(txIDs)) < pageSize && iter.Next() {
		if bytes.Equal(idxKey, iter.Key()) {
			// This key has the next index to use, not a tx ID
			continue
		}

		// get the value, make sure its in the right format
		txIDBytes := iter.Value()
		if len(txIDBytes) != hashing.HashLen {
			return nil, fmt.Errorf("invalid tx ID %s", txIDBytes)
		}

		// get the  txID and append to our list
		var txID ids.ID
		copy(txID[:], txIDBytes)
		txIDs = append(txIDs, txID)
	}
	return txIDs, nil
}

// Clear clears data about which balances [txID] changed.
func (i *indexer) Clear(txID ids.ID) {
	i.balanceChanges[txID] = make(map[ids.ShortID]map[ids.ID]struct{})
}

// Init initialises indexing, returning error if the state is invalid
func (i *indexer) init(allowIncomplete bool) error {
	return checkIndexStatus(i.db, true, allowIncomplete)
}

// checkIndexStatus checks the indexing status in the database, returning error if the state
// with respect to provided parameters is invalid
func checkIndexStatus(db database.KeyValueReaderWriter, enableIndexing, allowIncomplete bool) error {
	// verify whether the index is complete.
	idxComplete, err := database.GetBool(db, idxCompleteKey)
	if err == database.ErrNotFound {
		// We've not run before. Mark whether indexing is enabled this run.
		return database.PutBool(db, idxCompleteKey, enableIndexing)
	} else if err != nil {
		return err
	}

	if !idxComplete { // In a previous run, we did not index so it's incomplete.
		if enableIndexing && !allowIncomplete {
			// indexing was disabled before but now we want to index.
			return errors.New("running would create incomplete index. Allow incomplete indices or re-sync from genesis with indexing enabled")
		}
		// either indexing is disabled, or incomplete indices are ok, so we don't care that index is incomplete
		return nil
	}

	// the index is complete
	if !enableIndexing { // indexing is disabled this run
		if !allowIncomplete {
			return errors.New("running would create incomplete index. Allow incomplete indices or enable indexing")
		}
		// running without indexing makes it incomplete
		return database.PutBool(db, idxCompleteKey, false)
	}
	return nil
}

type noIndexer struct{}

func NewNoIndexer(db database.Database, allowIncomplete bool) (AddressTxsIndexer, error) {
	if err := checkIndexStatus(db, false, allowIncomplete); err != nil {
		return nil, err
	}
	return &noIndexer{}, nil
}

func (i *noIndexer) Add(ids.ID, []*avax.UTXOID, []*avax.UTXO, func(utxoID *avax.UTXOID) (*avax.UTXO, error)) error {
	return nil
}

func (i *noIndexer) Accept(ids.ID) error {
	return nil
}

func (i *noIndexer) Clear(ids.ID) {}

func (i *noIndexer) Read(ids.ShortID, ids.ID, uint64, uint64) ([]ids.ID, error) {
	return nil, nil
}
<|endoftext|>


<commit_before>package driver

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"code.cloudfoundry.org/lager"
)

type BtrFSDriver struct {
	logger   lager.Logger
	btrfsBin string
}

func NewBtrFSDriver(
	logger lager.Logger,
	btrfsBin string,
) *BtrFSDriver {
	return &BtrFSDriver{
		logger:   logger,
		btrfsBin: btrfsBin,
	}
}

func (driver *BtrFSDriver) CreateVolume(path string) error {
	_, _, err := driver.run(driver.btrfsBin, "subvolume", "create", path)
	if err != nil {
		return err
	}

	return nil
}

func (driver *BtrFSDriver) DestroyVolume(path string) error {
	volumePathsToDelete := []string{}

	findSubvolumes := func(p string, f os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !f.IsDir() {
			return nil
		}

		isSub, err := isSubvolume(p)
		if err != nil {
			return fmt.Errorf("failed to check if %s is a subvolume: %s", p, err)
		}

		if isSub {
			volumePathsToDelete = append(volumePathsToDelete, p)
		}

		return nil
	}

	if err := filepath.Walk(path, findSubvolumes); err != nil {
		return fmt.Errorf("recursively walking subvolumes for %s failed: %v", path, err)
	}

	for i := len(volumePathsToDelete) - 1; i >= 0; i-- {
		_, _, err := driver.run(driver.btrfsBin, "subvolume", "delete", volumePathsToDelete[i])
		if err != nil {
			return err
		}
	}

	return nil
}

func (driver *BtrFSDriver) CreateCopyOnWriteLayer(path string, parent string) error {
	_, _, err := driver.run(driver.btrfsBin, "subvolume", "snapshot", parent, path)
	return err
}

func (driver *BtrFSDriver) GetVolumeSizeInBytes(path string) (int64, error) {
	stdout := &bytes.Buffer{}
	cmd := exec.Command("du", "-s", path)
	cmd.Stdout = stdout

	err := cmd.Run()
	if err != nil {
		return 0, err
	}

	var size int64
	_, err = fmt.Sscanf(stdout.String(), "%d", &size)
	if err != nil {
		return 0, err
	}

	return size * 512, nil
}

func (driver *BtrFSDriver) run(command string, args ...string) (string, string, error) {
	cmd := exec.Command(command, args...)

	logger := driver.logger.Session("run-command", lager.Data{
		"command": command,
		"args":    args,
	})

	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	cmd.Stdout = stdout
	cmd.Stderr = stderr

	err := cmd.Run()

	loggerData := lager.Data{
		"stdout": stdout.String(),
		"stderr": stderr.String(),
	}

	if err != nil {
		logger.Error("failed", err, loggerData)
		return "", "", err
	}

	logger.Debug("ran", loggerData)

	return stdout.String(), stderr.String(), nil
}
<commit_msg>use 1024-byte blocks for detecting size on linux<commit_after>package driver

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"code.cloudfoundry.org/lager"
)

type BtrFSDriver struct {
	logger   lager.Logger
	btrfsBin string
}

func NewBtrFSDriver(
	logger lager.Logger,
	btrfsBin string,
) *BtrFSDriver {
	return &BtrFSDriver{
		logger:   logger,
		btrfsBin: btrfsBin,
	}
}

func (driver *BtrFSDriver) CreateVolume(path string) error {
	_, _, err := driver.run(driver.btrfsBin, "subvolume", "create", path)
	if err != nil {
		return err
	}

	return nil
}

func (driver *BtrFSDriver) DestroyVolume(path string) error {
	volumePathsToDelete := []string{}

	findSubvolumes := func(p string, f os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if !f.IsDir() {
			return nil
		}

		isSub, err := isSubvolume(p)
		if err != nil {
			return fmt.Errorf("failed to check if %s is a subvolume: %s", p, err)
		}

		if isSub {
			volumePathsToDelete = append(volumePathsToDelete, p)
		}

		return nil
	}

	if err := filepath.Walk(path, findSubvolumes); err != nil {
		return fmt.Errorf("recursively walking subvolumes for %s failed: %v", path, err)
	}

	for i := len(volumePathsToDelete) - 1; i >= 0; i-- {
		_, _, err := driver.run(driver.btrfsBin, "subvolume", "delete", volumePathsToDelete[i])
		if err != nil {
			return err
		}
	}

	return nil
}

func (driver *BtrFSDriver) CreateCopyOnWriteLayer(path string, parent string) error {
	_, _, err := driver.run(driver.btrfsBin, "subvolume", "snapshot", parent, path)
	return err
}

func (driver *BtrFSDriver) GetVolumeSizeInBytes(path string) (int64, error) {
	stdout := &bytes.Buffer{}
	cmd := exec.Command("du", "-sk", path)
	cmd.Stdout = stdout

	err := cmd.Run()
	if err != nil {
		return 0, err
	}

	var size int64
	_, err = fmt.Sscanf(stdout.String(), "%d", &size)
	if err != nil {
		return 0, err
	}

	return size * 512, nil
}

func (driver *BtrFSDriver) run(command string, args ...string) (string, string, error) {
	cmd := exec.Command(command, args...)

	logger := driver.logger.Session("run-command", lager.Data{
		"command": command,
		"args":    args,
	})

	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}

	cmd.Stdout = stdout
	cmd.Stderr = stderr

	err := cmd.Run()

	loggerData := lager.Data{
		"stdout": stdout.String(),
		"stderr": stderr.String(),
	}

	if err != nil {
		logger.Error("failed", err, loggerData)
		return "", "", err
	}

	logger.Debug("ran", loggerData)

	return stdout.String(), stderr.String(), nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package description

import (
	"time"

	"github.com/juju/version"
	"gopkg.in/juju/names.v2"
)

// HasAnnotations defines the common methods for setting and
// getting annotations for the various entities.
type HasAnnotations interface {
	Annotations() map[string]string
	SetAnnotations(map[string]string)
}

// HasConstraints defines the common methods for setting and
// getting constraints for the various entities.
type HasConstraints interface {
	Constraints() Constraints
	SetConstraints(ConstraintsArgs)
}

// HasStatusHistory defines the common methods for setting and
// getting historical status entries for the various entities.
type HasStatusHistory interface {
	StatusHistory() []Status
	SetStatusHistory([]StatusArgs)
}

// Model is a database agnostic representation of an existing model.
type Model interface {
	HasAnnotations
	HasConstraints

	Cloud() string
	CloudRegion() string
	CloudCredential() string
	Tag() names.ModelTag
	Owner() names.UserTag
	Config() map[string]interface{}
	LatestToolsVersion() version.Number

	// UpdateConfig overwrites existing config values with those specified.
	UpdateConfig(map[string]interface{})

	// Blocks returns a map of block type to the message associated with that
	// block.
	Blocks() map[string]string

	Users() []User
	AddUser(UserArgs)

	Machines() []Machine
	AddMachine(MachineArgs) Machine

	Applications() []Application
	AddApplication(ApplicationArgs) Application

	Relations() []Relation
	AddRelation(RelationArgs) Relation

	Spaces() []Space
	AddSpace(SpaceArgs) Space

	LinkLayerDevices() []LinkLayerDevice
	AddLinkLayerDevice(LinkLayerDeviceArgs) LinkLayerDevice

	Subnets() []Subnet
	AddSubnet(SubnetArgs) Subnet

	IPAddresses() []IPAddress
	AddIPAddress(IPAddressArgs) IPAddress

	SSHHostKeys() []SSHHostKey
	AddSSHHostKey(SSHHostKeyArgs) SSHHostKey

	Sequences() map[string]int
	SetSequence(name string, value int)

	Validate() error
}

// User represents a user of the model. Users are able to connect to, and
// depending on the read only flag, modify the model.
type User interface {
	Name() names.UserTag
	DisplayName() string
	CreatedBy() names.UserTag
	DateCreated() time.Time
	LastConnection() time.Time
	IsReadOnly() bool
	IsReadWrite() bool
	IsAdmin() bool
}

// Address represents an IP Address of some form.
type Address interface {
	Value() string
	Type() string
	Scope() string
	Origin() string
}

// AgentTools represent the version and related binary file
// that the machine and unit agents are using.
type AgentTools interface {
	Version() version.Binary
	URL() string
	SHA256() string
	Size() int64
}

// Machine represents an existing live machine or container running in the
// model.
type Machine interface {
	HasAnnotations
	HasConstraints
	HasStatusHistory

	Id() string
	Tag() names.MachineTag
	Nonce() string
	PasswordHash() string
	Placement() string
	Series() string
	ContainerType() string
	Jobs() []string
	SupportedContainers() ([]string, bool)

	Instance() CloudInstance
	SetInstance(CloudInstanceArgs)

	// Life() string -- only transmit alive things?
	ProviderAddresses() []Address
	MachineAddresses() []Address
	SetAddresses(machine []AddressArgs, provider []AddressArgs)

	PreferredPublicAddress() Address
	PreferredPrivateAddress() Address
	SetPreferredAddresses(public AddressArgs, private AddressArgs)

	Tools() AgentTools
	SetTools(AgentToolsArgs)

	Containers() []Machine
	AddContainer(MachineArgs) Machine

	Status() Status
	SetStatus(StatusArgs)

	// TODO:
	// Storage

	BlockDevices() []BlockDevice
	AddBlockDevice(BlockDeviceArgs) BlockDevice

	OpenedPorts() []OpenedPorts
	AddOpenedPorts(OpenedPortsArgs) OpenedPorts

	// THINKING: Validate() error to make sure the machine has
	// enough stuff set, like tools, and addresses etc.
	Validate() error

	// reboot doc
	// block devices
	// port docs
	// machine filesystems
}

// OpenedPorts represents a collection of port ranges that are open on a
// particular subnet. OpenedPorts are always associated with a Machine.
type OpenedPorts interface {
	SubnetID() string
	OpenPorts() []PortRange
}

// PortRange represents one or more contiguous ports opened by a particular
// Unit.
type PortRange interface {
	UnitName() string
	FromPort() int
	ToPort() int
	Protocol() string
}

// CloudInstance holds information particular to a machine
// instance in a cloud.
type CloudInstance interface {
	InstanceId() string
	Status() string
	Architecture() string
	Memory() uint64
	RootDisk() uint64
	CpuCores() uint64
	CpuPower() uint64
	Tags() []string
	AvailabilityZone() string
}

// Constraints holds information about particular deployment
// constraints for entities.
type Constraints interface {
	Architecture() string
	Container() string
	CpuCores() uint64
	CpuPower() uint64
	InstanceType() string
	Memory() uint64
	RootDisk() uint64

	Spaces() []string
	Tags() []string

	VirtType() string
}

// Status represents an agent, application, or workload status.
type Status interface {
	Value() string
	Message() string
	Data() map[string]interface{}
	Updated() time.Time
}

// Application represents a deployed charm in a model.
type Application interface {
	HasAnnotations
	HasConstraints
	HasStatusHistory

	Tag() names.ApplicationTag
	Name() string
	Series() string
	Subordinate() bool
	CharmURL() string
	Channel() string
	CharmModifiedVersion() int
	ForceCharm() bool
	Exposed() bool
	MinUnits() int

	Settings() map[string]interface{}
	SettingsRefCount() int

	Leader() string
	LeadershipSettings() map[string]interface{}

	MetricsCredentials() []byte

	Status() Status
	SetStatus(StatusArgs)

	Units() []Unit
	AddUnit(UnitArgs) Unit

	Validate() error
}

// Unit represents an instance of an application in a model.
type Unit interface {
	HasAnnotations
	HasConstraints

	Tag() names.UnitTag
	Name() string
	Machine() names.MachineTag

	PasswordHash() string

	Principal() names.UnitTag
	Subordinates() []names.UnitTag

	MeterStatusCode() string
	MeterStatusInfo() string

	// TODO: storage

	Tools() AgentTools
	SetTools(AgentToolsArgs)

	WorkloadStatus() Status
	SetWorkloadStatus(StatusArgs)

	WorkloadStatusHistory() []Status
	SetWorkloadStatusHistory([]StatusArgs)

	WorkloadVersion() string

	WorkloadVersionHistory() []Status
	SetWorkloadVersionHistory([]StatusArgs)

	AgentStatus() Status
	SetAgentStatus(StatusArgs)

	AgentStatusHistory() []Status
	SetAgentStatusHistory([]StatusArgs)

	Validate() error
}

// Relation represents a relationship between two applications,
// or a peer relation between different instances of an application.
type Relation interface {
	Id() int
	Key() string

	Endpoints() []Endpoint
	AddEndpoint(EndpointArgs) Endpoint
}

// Endpoint represents one end of a relation. A named endpoint provided
// by the charm that is deployed for the application.
type Endpoint interface {
	ApplicationName() string
	Name() string
	// Role, Interface, Optional, Limit, and Scope should all be available
	// through the Charm associated with the Application. There is no real need
	// for this information to be denormalised like this. However, for now,
	// since the import may well take place before the charms have been loaded
	// into the model, we'll send this information over.
	Role() string
	Interface() string
	Optional() bool
	Limit() int
	Scope() string

	// UnitCount returns the number of units the endpoint has settings for.
	UnitCount() int

	Settings(unitName string) map[string]interface{}
	SetUnitSettings(unitName string, settings map[string]interface{})
}

// Space represents a network space, which is a named collection of subnets.
type Space interface {
	Name() string
	Public() bool
	ProviderID() string
}

// LinkLayerDevice represents a link layer device.
type LinkLayerDevice interface {
	Name() string
	MTU() uint
	ProviderID() string
	MachineID() string
	Type() string
	MACAddress() string
	IsAutoStart() bool
	IsUp() bool
	ParentName() string
}

// Subnet represents a network subnet.
type Subnet interface {
	ProviderId() string
	CIDR() string
	VLANTag() int
	AvailabilityZone() string
	SpaceName() string
	AllocatableIPHigh() string
	AllocatableIPLow() string
}

// IPAddress represents an IP address.
type IPAddress interface {
	ProviderID() string
	DeviceName() string
	MachineID() string
	SubnetCIDR() string
	ConfigMethod() string
	Value() string
	DNSServers() []string
	DNSSearchDomains() []string
	GatewayAddress() string
}

// SSHHostKey represents an ssh host key.
type SSHHostKey interface {
	MachineID() string
	Keys() []string
}
<commit_msg>Initial volume interface.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package description

import (
	"time"

	"github.com/juju/version"
	"gopkg.in/juju/names.v2"
)

// HasAnnotations defines the common methods for setting and
// getting annotations for the various entities.
type HasAnnotations interface {
	Annotations() map[string]string
	SetAnnotations(map[string]string)
}

// HasConstraints defines the common methods for setting and
// getting constraints for the various entities.
type HasConstraints interface {
	Constraints() Constraints
	SetConstraints(ConstraintsArgs)
}

// HasStatus defines the common methods for setting and getting  status
// entries for the various entities.
type HasStatus interface {
	Status() Status
	SetStatus(StatusArgs)
}

// HasStatusHistory defines the common methods for setting and
// getting historical status entries for the various entities.
type HasStatusHistory interface {
	StatusHistory() []Status
	SetStatusHistory([]StatusArgs)
}

// Model is a database agnostic representation of an existing model.
type Model interface {
	HasAnnotations
	HasConstraints

	Cloud() string
	CloudRegion() string
	CloudCredential() string
	Tag() names.ModelTag
	Owner() names.UserTag
	Config() map[string]interface{}
	LatestToolsVersion() version.Number

	// UpdateConfig overwrites existing config values with those specified.
	UpdateConfig(map[string]interface{})

	// Blocks returns a map of block type to the message associated with that
	// block.
	Blocks() map[string]string

	Users() []User
	AddUser(UserArgs)

	Machines() []Machine
	AddMachine(MachineArgs) Machine

	Applications() []Application
	AddApplication(ApplicationArgs) Application

	Relations() []Relation
	AddRelation(RelationArgs) Relation

	Spaces() []Space
	AddSpace(SpaceArgs) Space

	LinkLayerDevices() []LinkLayerDevice
	AddLinkLayerDevice(LinkLayerDeviceArgs) LinkLayerDevice

	Subnets() []Subnet
	AddSubnet(SubnetArgs) Subnet

	IPAddresses() []IPAddress
	AddIPAddress(IPAddressArgs) IPAddress

	SSHHostKeys() []SSHHostKey
	AddSSHHostKey(SSHHostKeyArgs) SSHHostKey

	Sequences() map[string]int
	SetSequence(name string, value int)

	Validate() error
}

// User represents a user of the model. Users are able to connect to, and
// depending on the read only flag, modify the model.
type User interface {
	Name() names.UserTag
	DisplayName() string
	CreatedBy() names.UserTag
	DateCreated() time.Time
	LastConnection() time.Time
	IsReadOnly() bool
	IsReadWrite() bool
	IsAdmin() bool
}

// Address represents an IP Address of some form.
type Address interface {
	Value() string
	Type() string
	Scope() string
	Origin() string
}

// AgentTools represent the version and related binary file
// that the machine and unit agents are using.
type AgentTools interface {
	Version() version.Binary
	URL() string
	SHA256() string
	Size() int64
}

// Machine represents an existing live machine or container running in the
// model.
type Machine interface {
	HasAnnotations
	HasConstraints
	HasStatus
	HasStatusHistory

	Id() string
	Tag() names.MachineTag
	Nonce() string
	PasswordHash() string
	Placement() string
	Series() string
	ContainerType() string
	Jobs() []string
	SupportedContainers() ([]string, bool)

	Instance() CloudInstance
	SetInstance(CloudInstanceArgs)

	// Life() string -- only transmit alive things?
	ProviderAddresses() []Address
	MachineAddresses() []Address
	SetAddresses(machine []AddressArgs, provider []AddressArgs)

	PreferredPublicAddress() Address
	PreferredPrivateAddress() Address
	SetPreferredAddresses(public AddressArgs, private AddressArgs)

	Tools() AgentTools
	SetTools(AgentToolsArgs)

	Containers() []Machine
	AddContainer(MachineArgs) Machine

	// TODO:
	// Storage

	BlockDevices() []BlockDevice
	AddBlockDevice(BlockDeviceArgs) BlockDevice

	OpenedPorts() []OpenedPorts
	AddOpenedPorts(OpenedPortsArgs) OpenedPorts

	// THINKING: Validate() error to make sure the machine has
	// enough stuff set, like tools, and addresses etc.
	Validate() error

	// reboot doc
	// block devices
	// port docs
	// machine filesystems
}

// OpenedPorts represents a collection of port ranges that are open on a
// particular subnet. OpenedPorts are always associated with a Machine.
type OpenedPorts interface {
	SubnetID() string
	OpenPorts() []PortRange
}

// PortRange represents one or more contiguous ports opened by a particular
// Unit.
type PortRange interface {
	UnitName() string
	FromPort() int
	ToPort() int
	Protocol() string
}

// CloudInstance holds information particular to a machine
// instance in a cloud.
type CloudInstance interface {
	InstanceId() string
	Status() string
	Architecture() string
	Memory() uint64
	RootDisk() uint64
	CpuCores() uint64
	CpuPower() uint64
	Tags() []string
	AvailabilityZone() string
}

// Constraints holds information about particular deployment
// constraints for entities.
type Constraints interface {
	Architecture() string
	Container() string
	CpuCores() uint64
	CpuPower() uint64
	InstanceType() string
	Memory() uint64
	RootDisk() uint64

	Spaces() []string
	Tags() []string

	VirtType() string
}

// Status represents an agent, application, or workload status.
type Status interface {
	Value() string
	Message() string
	Data() map[string]interface{}
	Updated() time.Time
}

// Application represents a deployed charm in a model.
type Application interface {
	HasAnnotations
	HasConstraints
	HasStatus
	HasStatusHistory

	Tag() names.ApplicationTag
	Name() string
	Series() string
	Subordinate() bool
	CharmURL() string
	Channel() string
	CharmModifiedVersion() int
	ForceCharm() bool
	Exposed() bool
	MinUnits() int

	Settings() map[string]interface{}
	SettingsRefCount() int

	Leader() string
	LeadershipSettings() map[string]interface{}

	MetricsCredentials() []byte

	Units() []Unit
	AddUnit(UnitArgs) Unit

	Validate() error
}

// Unit represents an instance of an application in a model.
type Unit interface {
	HasAnnotations
	HasConstraints

	Tag() names.UnitTag
	Name() string
	Machine() names.MachineTag

	PasswordHash() string

	Principal() names.UnitTag
	Subordinates() []names.UnitTag

	MeterStatusCode() string
	MeterStatusInfo() string

	// TODO: storage

	Tools() AgentTools
	SetTools(AgentToolsArgs)

	WorkloadStatus() Status
	SetWorkloadStatus(StatusArgs)

	WorkloadStatusHistory() []Status
	SetWorkloadStatusHistory([]StatusArgs)

	WorkloadVersion() string

	WorkloadVersionHistory() []Status
	SetWorkloadVersionHistory([]StatusArgs)

	AgentStatus() Status
	SetAgentStatus(StatusArgs)

	AgentStatusHistory() []Status
	SetAgentStatusHistory([]StatusArgs)

	Validate() error
}

// Relation represents a relationship between two applications,
// or a peer relation between different instances of an application.
type Relation interface {
	Id() int
	Key() string

	Endpoints() []Endpoint
	AddEndpoint(EndpointArgs) Endpoint
}

// Endpoint represents one end of a relation. A named endpoint provided
// by the charm that is deployed for the application.
type Endpoint interface {
	ApplicationName() string
	Name() string
	// Role, Interface, Optional, Limit, and Scope should all be available
	// through the Charm associated with the Application. There is no real need
	// for this information to be denormalised like this. However, for now,
	// since the import may well take place before the charms have been loaded
	// into the model, we'll send this information over.
	Role() string
	Interface() string
	Optional() bool
	Limit() int
	Scope() string

	// UnitCount returns the number of units the endpoint has settings for.
	UnitCount() int

	Settings(unitName string) map[string]interface{}
	SetUnitSettings(unitName string, settings map[string]interface{})
}

// Space represents a network space, which is a named collection of subnets.
type Space interface {
	Name() string
	Public() bool
	ProviderID() string
}

// LinkLayerDevice represents a link layer device.
type LinkLayerDevice interface {
	Name() string
	MTU() uint
	ProviderID() string
	MachineID() string
	Type() string
	MACAddress() string
	IsAutoStart() bool
	IsUp() bool
	ParentName() string
}

// Subnet represents a network subnet.
type Subnet interface {
	ProviderId() string
	CIDR() string
	VLANTag() int
	AvailabilityZone() string
	SpaceName() string
	AllocatableIPHigh() string
	AllocatableIPLow() string
}

// IPAddress represents an IP address.
type IPAddress interface {
	ProviderID() string
	DeviceName() string
	MachineID() string
	SubnetCIDR() string
	ConfigMethod() string
	Value() string
	DNSServers() []string
	DNSSearchDomains() []string
	GatewayAddress() string
}

// SSHHostKey represents an ssh host key.
type SSHHostKey interface {
	MachineID() string
	Keys() []string
}

// Volume represents a volume (disk, logical volume, etc.) in the model.
type Volume interface {
	HasStatus
	HasStatusHistory

	Tag() names.VolumeTag
	Name() string
	// Link to Storage...

	Binding() names.Tag

	Provisioned() bool

	Size() uint64
	Pool() string

	// These three attributes are only valid if the volume is provisioned.

	HardwareID() string
	VolumeID() string
	Persistent() bool

	Attachments() []VolumeAttachment
}

// VolumeAttachment represents a volume attached to a machine.
type VolumeAttachment interface {
	MachineID() string

	ReadOnly() bool
	DeviceName() string
	DeviceLink() string
	BusAddress() string
}
<|endoftext|>


<commit_before>/**
 * Copyright 2015 @ z3q.net.
 * name : express
 * author : jarryliu
 * date : 2016-07-05 15:56
 * description :
 * history :
 */
package express

import (
	"go2o/core/domain/interface/express"
	"go2o/core/domain/interface/valueobject"
	"go2o/core/infrastructure/domain"
	"strconv"
	"strings"
	"sync"
)

const (
	areaDelimer = ","
)

var _ express.IUserExpress = new(userExpressImpl)

type userExpressImpl struct {
	_userId int
	_arr    []express.IExpressTemplate
	_rep    express.IExpressRep
	_valRep valueobject.IValueRep
}

func NewUserExpress(userId int, rep express.IExpressRep,
	valRep valueobject.IValueRep) express.IUserExpress {
	return &userExpressImpl{
		_userId: userId,
		_rep:    rep,
		_valRep: valRep,
	}
}

// 获取聚合根编号
func (this *userExpressImpl) GetAggregateRootId() int {
	return this._userId
}

// 创建快递模板
func (this *userExpressImpl) CreateTemplate(t *express.ExpressTemplate) express.IExpressTemplate {
	t.UserId = this.GetAggregateRootId()
	return newExpressTemplate(this, t, this._rep, this._valRep)
}

// 获取快递模板
func (this *userExpressImpl) GetTemplate(id int) express.IExpressTemplate {
	for _, v := range this.GetAllTemplate() {
		if v.GetDomainId() == id {
			return v
		}
	}
	return nil
}

// 获取所有的快递模板
func (this *userExpressImpl) GetAllTemplate() []express.IExpressTemplate {
	if this._arr == nil {
		list := this._rep.GetUserAllTemplate(this.GetAggregateRootId())
		this._arr = make([]express.IExpressTemplate, len(list))
		for i, v := range list {
			this._arr[i] = this.CreateTemplate(v)
		}
	}
	return this._arr
}

// 删除模板
func (this *userExpressImpl) DeleteTemplate(id int) error {
	for i, v := range this.GetAllTemplate() {
		if v.GetDomainId() == id {
			err := this._rep.DeleteExpressTemplate(
				this.GetAggregateRootId(), v.GetDomainId())
			if err == nil {
				this._arr = append(this._arr[:i], this._arr[i+1:]...)
			}
			return err
		}
	}
	return nil
}

// 计算快递运费
func (this *userExpressImpl) mathFee(basis int, unit int, firstUnit int,
	firstFee float32, addUnix int, addFee float32) float32 {
	outUnit := unit - firstUnit
	if outUnit > 0 {
		// 如果超过首次计量,则获取超出倍数,叠加计费
		outTimes := outUnit / addUnix
		if outUnit%addUnix > 0 {
			outTimes += 1
		}
		return firstFee + float32(outTimes)*addFee
	}
	return firstFee

}

// 获取快递费,传入地区编码，根据单位值，如总重量。
func (this *userExpressImpl) GetExpressFee(templateId int, areaCode string, unit int) float32 {
	tpl := this.GetTemplate(templateId)
	if tpl == nil {
		//return 0
		//todo: 仅仅为测试,如果未指定快递模板,则用默认的第一个
		if len(this.GetAllTemplate()) > 0 {
			tpl = this.GetTemplate(this.GetAllTemplate()[0].GetDomainId())
		} else {
			return 0
		}
	}
	v := tpl.Value()

	//log.Println("--------", this._userId, unit,v)

	//判断是否免邮
	if v.IsFree == 1 {
		return 0
	}
	//根据地区规则计算运费
	areaSet := tpl.GetAreaExpressTemplateByAreaCode(areaCode)
	if areaSet != nil {
		return this.mathFee(v.Basis, unit, areaSet.FirstUnit, areaSet.FirstFee,
			areaSet.AddUnit, areaSet.AddFee)
	}
	//根据默认规则计算运费
	return this.mathFee(v.Basis, unit, v.FirstUnit, v.FirstFee,
		v.AddUnit, v.AddFee)
}

var _ express.IExpressTemplate = new(expressTemplateImpl)

type expressTemplateImpl struct {
	_value       *express.ExpressTemplate
	_userExpress *userExpressImpl
	_rep         express.IExpressRep
	_areaList    []express.ExpressAreaTemplate
	_areaMap     map[string]*express.ExpressAreaTemplate
	_mux         sync.Mutex
	_valRep      valueobject.IValueRep
}

func newExpressTemplate(u *userExpressImpl, v *express.ExpressTemplate,
	rep express.IExpressRep, valRep valueobject.IValueRep) express.IExpressTemplate {
	return &expressTemplateImpl{
		_value:       v,
		_userExpress: u,
		_rep:         rep,
		_valRep:      valRep,
	}
}

// 获取领域对象编号
func (this *expressTemplateImpl) GetDomainId() int {
	return this._value.Id
}

// 获取快递模板数据
func (this *expressTemplateImpl) Value() express.ExpressTemplate {
	return *this._value
}

// 设置地区的快递模板
func (this *expressTemplateImpl) Set(v *express.ExpressTemplate) error {
	if this._value.UserId != v.UserId ||
		v.Basis <= 0 || len(v.Name) == 0 ||
		v.AddFee <= 0 || v.AddUnit <= 0 ||
		v.FirstUnit <= 0 {
		return express.ErrNotFullExpressTemplate
	}
	this._value = v
	return nil
}

// 保存
func (this *expressTemplateImpl) Save() (int, error) {
	id, err := this._rep.SaveExpressTemplate(this._value)
	if err == nil {
		this._value.Id = id
		this._userExpress._arr = nil
	}
	return id, err
}

// 保存地区快递模板
func (this *expressTemplateImpl) SaveAreaTemplate(t *express.ExpressAreaTemplate) (int, error) {
	this.GetAllAreaTemplate()
	arr := this.getAreaCodeArray(t.CodeList)
	if arr == nil {
		return 0, express.ErrExpressTemplateMissingAreaCode
	}
	intArr := make([]int, len(arr))
	for i, code := range arr {
		v, ok := this._areaMap[code]
		if ok && v.Id != t.Id {
			return 0, express.ErrExistsAreaTemplateSet
		}
		intArr[i] = strconv.Atoi(code)
	}
	// 获取对应的中文名称
	names := this._valRep.GetAreaNames(intArr)
	t.NameList = strings.Join(names, ",")

	// 保存,如果未出错,则更新缓存
	id, err := this._rep.SaveExpressTemplateAreaSet(t)
	if err == nil {
		this._areaList = nil
		this._areaMap = nil
		this.GetAllAreaTemplate()
	}
	return id, err
}

func (this *expressTemplateImpl) getAreaCodeArray(codeList string) []string {
	codeList = strings.Trim(codeList, " ")
	if codeList == "" {
		return nil
	}
	return strings.Split(codeList, areaDelimer)
}

// 初始化地区与运费的映射
func (this *expressTemplateImpl) initAreaMap() {
	this._areaMap = make(map[string]*express.ExpressAreaTemplate, len(this._areaList))
	for _, v := range this._areaList {
		arr := this.getAreaCodeArray(v.CodeList)
		if arr == nil {
			continue
		}
		// 遍历地区编号
		for _, code := range arr {
			if code == "" {
				continue
			}
			if _, ok := this._areaMap[code]; !ok {
				this._areaMap[code] = &v
			}
		}
	}
}

// 获取所有的地区快递模板
func (this *expressTemplateImpl) GetAllAreaTemplate() []express.ExpressAreaTemplate {
	this._mux.Lock()
	if this._areaList == nil {
		this._areaList = this._rep.GetExpressTemplateAllAreaSet(this.GetDomainId())
		this.initAreaMap()
	}
	this._mux.Unlock()
	return this._areaList
}

// 删除模板地区设定
func (this *expressTemplateImpl) DeleteAreaSet(areaSetId int) error {
	this.GetAllAreaTemplate()
	if this.GetAreaExpressTemplate(areaSetId) != nil {
		err := this._rep.DeleteAreaExpressTemplate(this.GetDomainId(), areaSetId)
		if err == nil {
			this._areaList = nil
			this._areaMap = nil
		}
		return err
	}
	return nil
}

// 根据地区编码获取运费模板
func (this *expressTemplateImpl) GetAreaExpressTemplateByAreaCode(areaCode string) *express.ExpressAreaTemplate {
	this.GetAllAreaTemplate()
	return this._areaMap[areaCode]
}

// 根据编号获取地区的运费模板
func (this *expressTemplateImpl) GetAreaExpressTemplate(id int) *express.ExpressAreaTemplate {
	for _, v := range this.GetAllAreaTemplate() {
		if v.Id == id {
			return &v
		}
	}
	return nil
}

type ExpressRepBase struct {
}

// 将默认的快递服务商保存
func (this *ExpressRepBase) SaveDefaultExpressProviders(rep express.IExpressRep) []*express.ExpressProvider {
	var err error
	for _, v := range express.SupportedExpressProvider {
		if v.Id, err = rep.SaveExpressProvider(v); err != nil {
			domain.HandleError(err, "domain")
		}
	}
	return express.SupportedExpressProvider
}
<commit_msg>express fix<commit_after>/**
 * Copyright 2015 @ z3q.net.
 * name : express
 * author : jarryliu
 * date : 2016-07-05 15:56
 * description :
 * history :
 */
package express

import (
	"go2o/core/domain/interface/express"
	"go2o/core/domain/interface/valueobject"
	"go2o/core/infrastructure/domain"
	"strconv"
	"strings"
	"sync"
)

const (
	areaDelimer = ","
)

var _ express.IUserExpress = new(userExpressImpl)

type userExpressImpl struct {
	_userId int
	_arr    []express.IExpressTemplate
	_rep    express.IExpressRep
	_valRep valueobject.IValueRep
}

func NewUserExpress(userId int, rep express.IExpressRep,
	valRep valueobject.IValueRep) express.IUserExpress {
	return &userExpressImpl{
		_userId: userId,
		_rep:    rep,
		_valRep: valRep,
	}
}

// 获取聚合根编号
func (this *userExpressImpl) GetAggregateRootId() int {
	return this._userId
}

// 创建快递模板
func (this *userExpressImpl) CreateTemplate(t *express.ExpressTemplate) express.IExpressTemplate {
	t.UserId = this.GetAggregateRootId()
	return newExpressTemplate(this, t, this._rep, this._valRep)
}

// 获取快递模板
func (this *userExpressImpl) GetTemplate(id int) express.IExpressTemplate {
	for _, v := range this.GetAllTemplate() {
		if v.GetDomainId() == id {
			return v
		}
	}
	return nil
}

// 获取所有的快递模板
func (this *userExpressImpl) GetAllTemplate() []express.IExpressTemplate {
	if this._arr == nil {
		list := this._rep.GetUserAllTemplate(this.GetAggregateRootId())
		this._arr = make([]express.IExpressTemplate, len(list))
		for i, v := range list {
			this._arr[i] = this.CreateTemplate(v)
		}
	}
	return this._arr
}

// 删除模板
func (this *userExpressImpl) DeleteTemplate(id int) error {
	for i, v := range this.GetAllTemplate() {
		if v.GetDomainId() == id {
			err := this._rep.DeleteExpressTemplate(
				this.GetAggregateRootId(), v.GetDomainId())
			if err == nil {
				this._arr = append(this._arr[:i], this._arr[i+1:]...)
			}
			return err
		}
	}
	return nil
}

// 计算快递运费
func (this *userExpressImpl) mathFee(basis int, unit int, firstUnit int,
	firstFee float32, addUnix int, addFee float32) float32 {
	outUnit := unit - firstUnit
	if outUnit > 0 {
		// 如果超过首次计量,则获取超出倍数,叠加计费
		outTimes := outUnit / addUnix
		if outUnit%addUnix > 0 {
			outTimes += 1
		}
		return firstFee + float32(outTimes)*addFee
	}
	return firstFee

}

// 获取快递费,传入地区编码，根据单位值，如总重量。
func (this *userExpressImpl) GetExpressFee(templateId int, areaCode string, unit int) float32 {
	tpl := this.GetTemplate(templateId)
	if tpl == nil {
		//return 0
		//todo: 仅仅为测试,如果未指定快递模板,则用默认的第一个
		if len(this.GetAllTemplate()) > 0 {
			tpl = this.GetTemplate(this.GetAllTemplate()[0].GetDomainId())
		} else {
			return 0
		}
	}
	v := tpl.Value()

	//log.Println("--------", this._userId, unit,v)

	//判断是否免邮
	if v.IsFree == 1 {
		return 0
	}
	//根据地区规则计算运费
	areaSet := tpl.GetAreaExpressTemplateByAreaCode(areaCode)
	if areaSet != nil {
		return this.mathFee(v.Basis, unit, areaSet.FirstUnit, areaSet.FirstFee,
			areaSet.AddUnit, areaSet.AddFee)
	}
	//根据默认规则计算运费
	return this.mathFee(v.Basis, unit, v.FirstUnit, v.FirstFee,
		v.AddUnit, v.AddFee)
}

var _ express.IExpressTemplate = new(expressTemplateImpl)

type expressTemplateImpl struct {
	_value       *express.ExpressTemplate
	_userExpress *userExpressImpl
	_rep         express.IExpressRep
	_areaList    []express.ExpressAreaTemplate
	_areaMap     map[string]*express.ExpressAreaTemplate
	_mux         sync.Mutex
	_valRep      valueobject.IValueRep
}

func newExpressTemplate(u *userExpressImpl, v *express.ExpressTemplate,
	rep express.IExpressRep, valRep valueobject.IValueRep) express.IExpressTemplate {
	return &expressTemplateImpl{
		_value:       v,
		_userExpress: u,
		_rep:         rep,
		_valRep:      valRep,
	}
}

// 获取领域对象编号
func (this *expressTemplateImpl) GetDomainId() int {
	return this._value.Id
}

// 获取快递模板数据
func (this *expressTemplateImpl) Value() express.ExpressTemplate {
	return *this._value
}

// 设置地区的快递模板
func (this *expressTemplateImpl) Set(v *express.ExpressTemplate) error {
	if this._value.UserId != v.UserId ||
		v.Basis <= 0 || len(v.Name) == 0 ||
		v.AddFee <= 0 || v.AddUnit <= 0 ||
		v.FirstUnit <= 0 {
		return express.ErrNotFullExpressTemplate
	}
	this._value = v
	return nil
}

// 保存
func (this *expressTemplateImpl) Save() (int, error) {
	id, err := this._rep.SaveExpressTemplate(this._value)
	if err == nil {
		this._value.Id = id
		this._userExpress._arr = nil
	}
	return id, err
}

// 保存地区快递模板
func (this *expressTemplateImpl) SaveAreaTemplate(t *express.ExpressAreaTemplate) (int, error) {
	this.GetAllAreaTemplate()
	arr := this.getAreaCodeArray(t.CodeList)
	if arr == nil {
		return 0, express.ErrExpressTemplateMissingAreaCode
	}
	intArr := make([]int, len(arr))
	for i, code := range arr {
		v, ok := this._areaMap[code]
		if ok && v.Id != t.Id {
			return 0, express.ErrExistsAreaTemplateSet
		}
		intArr[i],_ = strconv.Atoi(code)
	}
	// 获取对应的中文名称
	names := this._valRep.GetAreaNames(intArr)
	t.NameList = strings.Join(names, ",")

	// 保存,如果未出错,则更新缓存
	id, err := this._rep.SaveExpressTemplateAreaSet(t)
	if err == nil {
		this._areaList = nil
		this._areaMap = nil
		this.GetAllAreaTemplate()
	}
	return id, err
}

func (this *expressTemplateImpl) getAreaCodeArray(codeList string) []string {
	codeList = strings.Trim(codeList, " ")
	if codeList == "" {
		return nil
	}
	return strings.Split(codeList, areaDelimer)
}

// 初始化地区与运费的映射
func (this *expressTemplateImpl) initAreaMap() {
	this._areaMap = make(map[string]*express.ExpressAreaTemplate, len(this._areaList))
	for _, v := range this._areaList {
		arr := this.getAreaCodeArray(v.CodeList)
		if arr == nil {
			continue
		}
		// 遍历地区编号
		for _, code := range arr {
			if code == "" {
				continue
			}
			if _, ok := this._areaMap[code]; !ok {
				this._areaMap[code] = &v
			}
		}
	}
}

// 获取所有的地区快递模板
func (this *expressTemplateImpl) GetAllAreaTemplate() []express.ExpressAreaTemplate {
	this._mux.Lock()
	if this._areaList == nil {
		this._areaList = this._rep.GetExpressTemplateAllAreaSet(this.GetDomainId())
		this.initAreaMap()
	}
	this._mux.Unlock()
	return this._areaList
}

// 删除模板地区设定
func (this *expressTemplateImpl) DeleteAreaSet(areaSetId int) error {
	this.GetAllAreaTemplate()
	if this.GetAreaExpressTemplate(areaSetId) != nil {
		err := this._rep.DeleteAreaExpressTemplate(this.GetDomainId(), areaSetId)
		if err == nil {
			this._areaList = nil
			this._areaMap = nil
		}
		return err
	}
	return nil
}

// 根据地区编码获取运费模板
func (this *expressTemplateImpl) GetAreaExpressTemplateByAreaCode(areaCode string) *express.ExpressAreaTemplate {
	this.GetAllAreaTemplate()
	return this._areaMap[areaCode]
}

// 根据编号获取地区的运费模板
func (this *expressTemplateImpl) GetAreaExpressTemplate(id int) *express.ExpressAreaTemplate {
	for _, v := range this.GetAllAreaTemplate() {
		if v.Id == id {
			return &v
		}
	}
	return nil
}

type ExpressRepBase struct {
}

// 将默认的快递服务商保存
func (this *ExpressRepBase) SaveDefaultExpressProviders(rep express.IExpressRep) []*express.ExpressProvider {
	var err error
	for _, v := range express.SupportedExpressProvider {
		if v.Id, err = rep.SaveExpressProvider(v); err != nil {
			domain.HandleError(err, "domain")
		}
	}
	return express.SupportedExpressProvider
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/rdwilliamson/aws/glacier"
	"os"
)

func vault(args []string) {
	if len(args) < 1 {
		fmt.Println("no vault command")
		os.Exit(1)
	}
	command := args[0]
	args = args[1:]
	switch command {
	case "create", "delete", "describe":
		args = getConnection(args)

		if len(args) < 1 {
			fmt.Println("no vault name")
			os.Exit(1)
		}
		name := args[0]

		switch command {
		case "create":
			err := connection.CreateVault(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

		case "delete":
			err := connection.DeleteVault(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

		case "describe":
			vault, err := connection.DescribeVault(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Printf("%+v\n", vault)
		}

	case "list":
		args = getConnection(args)

		vaults, _, err := connection.ListVaults("", 0)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		for _, v := range vaults {
			fmt.Println("Vault Name:", v.VaultName)
			fmt.Println("Vault ARN:", v.VaultARN)
			fmt.Println("Creation Date:", v.CreationDate)
			fmt.Println("Archives;", v.NumberOfArchives)
			fmt.Println("Size:", v.SizeInBytes, prettySize(v.SizeInBytes))
			fmt.Println("Last Inventory Date:", v.LastInventoryDate)
			fmt.Println()
		}

	case "notifications":
		if len(args) < 1 {
			fmt.Println("no notification command")
			os.Exit(1)
		}
		subCommand := args[0]
		args = args[1:]

		args = getConnection(args)

		if len(args) < 1 {
			fmt.Println("no vault name")
			os.Exit(1)
		}
		name := args[0]
		args = args[1:]

		switch subCommand {
		case "set":
			if len(args) < 1 {
				fmt.Println("no notification topic")
				os.Exit(1)
			}
			topic := args[0]
			notifications := &glacier.Notifications{[]string{
				"ArchiveRetrievalCompleted", "InventoryRetrievalCompleted"},
				topic}
			err := connection.SetVaultNotifications(name, notifications)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

		case "get":
			notifications, err := connection.GetVaultNotifications(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Printf("%+v\n", notifications)

		case "delete":
			err := connection.DeleteVaultNotifications(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		default:
			fmt.Println("unknown notification command:", subCommand)
			os.Exit(1)
		}
	default:
		fmt.Println("unknown vault command:", command)
		os.Exit(1)
	}
}
<commit_msg>Cleaned up printing of notifications.<commit_after>package main

import (
	"fmt"
	"github.com/rdwilliamson/aws/glacier"
	"os"
)

func vault(args []string) {
	if len(args) < 1 {
		fmt.Println("no vault command")
		os.Exit(1)
	}
	command := args[0]
	args = args[1:]
	switch command {
	case "create", "delete", "describe":
		args = getConnection(args)

		if len(args) < 1 {
			fmt.Println("no vault name")
			os.Exit(1)
		}
		name := args[0]

		switch command {
		case "create":
			err := connection.CreateVault(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

		case "delete":
			err := connection.DeleteVault(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

		case "describe":
			vault, err := connection.DescribeVault(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			fmt.Printf("%+v\n", vault)
		}

	case "list":
		args = getConnection(args)

		vaults, _, err := connection.ListVaults("", 0)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}

		for _, v := range vaults {
			fmt.Println("Vault Name:", v.VaultName)
			fmt.Println("Vault ARN:", v.VaultARN)
			fmt.Println("Creation Date:", v.CreationDate)
			fmt.Println("Archives;", v.NumberOfArchives)
			fmt.Println("Size:", v.SizeInBytes, prettySize(v.SizeInBytes))
			fmt.Println("Last Inventory Date:", v.LastInventoryDate)
			fmt.Println()
		}

	case "notifications":
		if len(args) < 1 {
			fmt.Println("no notification command")
			os.Exit(1)
		}
		subCommand := args[0]
		args = args[1:]

		args = getConnection(args)

		if len(args) < 1 {
			fmt.Println("no vault name")
			os.Exit(1)
		}
		name := args[0]
		args = args[1:]

		switch subCommand {
		case "set":
			if len(args) < 1 {
				fmt.Println("no notification topic")
				os.Exit(1)
			}
			topic := args[0]
			notifications := &glacier.Notifications{[]string{
				"ArchiveRetrievalCompleted", "InventoryRetrievalCompleted"},
				topic}
			err := connection.SetVaultNotifications(name, notifications)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

		case "get":
			notifications, err := connection.GetVaultNotifications(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}

			fmt.Printf("Events: ")
			for _, v := range notifications.Events {
				fmt.Printf("%s ", v)
			}
			fmt.Println()
			fmt.Println("SNSTopic:", notifications.SNSTopic)

		case "delete":
			err := connection.DeleteVaultNotifications(name)
			if err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
		default:
			fmt.Println("unknown notification command:", subCommand)
			os.Exit(1)
		}
	default:
		fmt.Println("unknown vault command:", command)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package venom

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/yaml.v2"
)

// Version of Venom
// One Line for this, used by release.sh script
// Keep "const Version on one line"
const Version = "0.0.2"

// PrintFunc used by venom to print output
var PrintFunc = fmt.Printf

var (
	executors = map[string]Executor{}
	contexts  = map[string]TestCaseContext{}
)

const (
	// ContextKey is key for Test Case Context. this
	// can be used by executors for getting context
	ContextKey = "tcContext"
)

// RegisterExecutor register Test Executors
func RegisterExecutor(name string, e Executor) {
	executors[name] = e
}

// getExecutorWrap initializes a test by name
// no type -> exec is default
func getExecutorWrap(t map[string]interface{}, tcc TestCaseContext) (*executorWrap, error) {
	var name string
	var retry, delay, timeout int

	if itype, ok := t["type"]; ok {
		name = fmt.Sprintf("%s", itype)
	}

	if name == "" && tcc.GetName() != "default" {
		name = tcc.GetName()
	} else if name == "" {
		name = "exec"
	}

	retry, errRetry := getAttrInt(t, "retry")
	if errRetry != nil {
		return nil, errRetry
	}
	delay, errDelay := getAttrInt(t, "delay")
	if errDelay != nil {
		return nil, errDelay
	}
	timeout, errTimeout := getAttrInt(t, "timeout")
	if errTimeout != nil {
		return nil, errTimeout
	}

	if e, ok := executors[name]; ok {
		ew := &executorWrap{
			executor: e,
			retry:    retry,
			delay:    delay,
			timeout:  timeout,
		}
		return ew, nil
	}

	return nil, fmt.Errorf("[%s] type '%s' is not implemented", tcc.GetName(), name)
}

// RegisterTestCaseContext new register TestCaseContext
func RegisterTestCaseContext(name string, tcc TestCaseContext) {
	contexts[name] = tcc
}

// getContextWrap initializes a context for a testcase
// no type -> parent context
func getContextWrap(tc *TestCase) (TestCaseContext, error) {
	if tc.Context == nil {
		return contexts["default"], nil
	}
	var typeName string
	if itype, ok := tc.Context["type"]; ok {
		typeName = fmt.Sprintf("%s", itype)
	}

	if typeName == "" {
		return nil, fmt.Errorf("context type '%s' is not implemented", typeName)
	}
	contexts[typeName].SetTestCase(*tc)
	return contexts[typeName], nil
}

func getAttrInt(t map[string]interface{}, name string) (int, error) {
	var out int
	if i, ok := t[name]; ok {
		var ok bool
		out, ok = i.(int)
		if !ok {
			return -1, fmt.Errorf("attribute %s '%s' is not an integer", name, i)
		}
	}
	if out < 0 {
		out = 0
	}
	return out, nil
}

// Exit func display an error message on stderr and exit 1
func Exit(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(1)
}

// OutputResult output result to sdtout, files...
func OutputResult(format string, resume, resumeFailures bool, outputDir string, tests Tests, elapsed time.Duration, detailsLevel string) error {
	var data []byte
	var err error
	switch format {
	case "json":
		data, err = json.Marshal(tests)
		if err != nil {
			log.Fatalf("Error: cannot format output json (%s)", err)
		}
	case "yml", "yaml":
		data, err = yaml.Marshal(tests)
		if err != nil {
			log.Fatalf("Error: cannot format output yaml (%s)", err)
		}
	default:
		dataxml, errm := xml.Marshal(tests)
		if errm != nil {
			log.Fatalf("Error: cannot format xml output: %s", errm)
		}
		data = append([]byte("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"), dataxml...)
	}

	if detailsLevel == "high" {
		PrintFunc(string(data))
	}

	if resume {
		outputResume(tests, elapsed, resumeFailures)
	}

	if outputDir != "" {
		filename := outputDir + "/" + "test_results" + "." + format
		if err := ioutil.WriteFile(filename, data, 0644); err != nil {
			return fmt.Errorf("Error while creating file %s, err:%s", filename, err)
		}
	}
	return nil
}

func outputResume(tests Tests, elapsed time.Duration, resumeFailures bool) {

	if resumeFailures {
		for _, t := range tests.TestSuites {
			if t.Failures > 0 || t.Errors > 0 {
				PrintFunc("FAILED %s\n", t.Name)
				PrintFunc("--------------\n")

				for _, tc := range t.TestCases {
					for _, f := range tc.Failures {
						PrintFunc("%s\n", f.Value)
					}
					for _, f := range tc.Errors {
						PrintFunc("%s\n", f.Value)
					}
				}
				PrintFunc("-=-=-=-=-=-=-=-=-\n")
			}
		}
	}

	totalTestCases := 0
	totalTestSteps := 0
	for _, t := range tests.TestSuites {
		if t.Failures > 0 || t.Errors > 0 {
			PrintFunc("FAILED %s\n", t.Name)
		}
		totalTestCases += len(t.TestCases)
		for _, tc := range t.TestCases {
			totalTestSteps += len(tc.TestSteps)
		}
	}

	PrintFunc("Total:%d TotalOK:%d TotalKO:%d TotalSkipped:%d TotalTestSuite:%d TotalTestCase:%d TotalTestStep:%d Duration:%s\n",
		tests.Total,
		tests.TotalOK,
		tests.TotalKO,
		tests.TotalSkipped,
		len(tests.TestSuites),
		totalTestCases,
		totalTestSteps,
		elapsed,
	)
}
<commit_msg>[auto] bump version to v0.0.3<commit_after>package venom

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/yaml.v2"
)

// Version of Venom
// One Line for this, used by release.sh script
// Keep "const Version on one line"
const Version = "0.0.3"

// PrintFunc used by venom to print output
var PrintFunc = fmt.Printf

var (
	executors = map[string]Executor{}
	contexts  = map[string]TestCaseContext{}
)

const (
	// ContextKey is key for Test Case Context. this
	// can be used by executors for getting context
	ContextKey = "tcContext"
)

// RegisterExecutor register Test Executors
func RegisterExecutor(name string, e Executor) {
	executors[name] = e
}

// getExecutorWrap initializes a test by name
// no type -> exec is default
func getExecutorWrap(t map[string]interface{}, tcc TestCaseContext) (*executorWrap, error) {
	var name string
	var retry, delay, timeout int

	if itype, ok := t["type"]; ok {
		name = fmt.Sprintf("%s", itype)
	}

	if name == "" && tcc.GetName() != "default" {
		name = tcc.GetName()
	} else if name == "" {
		name = "exec"
	}

	retry, errRetry := getAttrInt(t, "retry")
	if errRetry != nil {
		return nil, errRetry
	}
	delay, errDelay := getAttrInt(t, "delay")
	if errDelay != nil {
		return nil, errDelay
	}
	timeout, errTimeout := getAttrInt(t, "timeout")
	if errTimeout != nil {
		return nil, errTimeout
	}

	if e, ok := executors[name]; ok {
		ew := &executorWrap{
			executor: e,
			retry:    retry,
			delay:    delay,
			timeout:  timeout,
		}
		return ew, nil
	}

	return nil, fmt.Errorf("[%s] type '%s' is not implemented", tcc.GetName(), name)
}

// RegisterTestCaseContext new register TestCaseContext
func RegisterTestCaseContext(name string, tcc TestCaseContext) {
	contexts[name] = tcc
}

// getContextWrap initializes a context for a testcase
// no type -> parent context
func getContextWrap(tc *TestCase) (TestCaseContext, error) {
	if tc.Context == nil {
		return contexts["default"], nil
	}
	var typeName string
	if itype, ok := tc.Context["type"]; ok {
		typeName = fmt.Sprintf("%s", itype)
	}

	if typeName == "" {
		return nil, fmt.Errorf("context type '%s' is not implemented", typeName)
	}
	contexts[typeName].SetTestCase(*tc)
	return contexts[typeName], nil
}

func getAttrInt(t map[string]interface{}, name string) (int, error) {
	var out int
	if i, ok := t[name]; ok {
		var ok bool
		out, ok = i.(int)
		if !ok {
			return -1, fmt.Errorf("attribute %s '%s' is not an integer", name, i)
		}
	}
	if out < 0 {
		out = 0
	}
	return out, nil
}

// Exit func display an error message on stderr and exit 1
func Exit(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
	os.Exit(1)
}

// OutputResult output result to sdtout, files...
func OutputResult(format string, resume, resumeFailures bool, outputDir string, tests Tests, elapsed time.Duration, detailsLevel string) error {
	var data []byte
	var err error
	switch format {
	case "json":
		data, err = json.Marshal(tests)
		if err != nil {
			log.Fatalf("Error: cannot format output json (%s)", err)
		}
	case "yml", "yaml":
		data, err = yaml.Marshal(tests)
		if err != nil {
			log.Fatalf("Error: cannot format output yaml (%s)", err)
		}
	default:
		dataxml, errm := xml.Marshal(tests)
		if errm != nil {
			log.Fatalf("Error: cannot format xml output: %s", errm)
		}
		data = append([]byte("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"), dataxml...)
	}

	if detailsLevel == "high" {
		PrintFunc(string(data))
	}

	if resume {
		outputResume(tests, elapsed, resumeFailures)
	}

	if outputDir != "" {
		filename := outputDir + "/" + "test_results" + "." + format
		if err := ioutil.WriteFile(filename, data, 0644); err != nil {
			return fmt.Errorf("Error while creating file %s, err:%s", filename, err)
		}
	}
	return nil
}

func outputResume(tests Tests, elapsed time.Duration, resumeFailures bool) {

	if resumeFailures {
		for _, t := range tests.TestSuites {
			if t.Failures > 0 || t.Errors > 0 {
				PrintFunc("FAILED %s\n", t.Name)
				PrintFunc("--------------\n")

				for _, tc := range t.TestCases {
					for _, f := range tc.Failures {
						PrintFunc("%s\n", f.Value)
					}
					for _, f := range tc.Errors {
						PrintFunc("%s\n", f.Value)
					}
				}
				PrintFunc("-=-=-=-=-=-=-=-=-\n")
			}
		}
	}

	totalTestCases := 0
	totalTestSteps := 0
	for _, t := range tests.TestSuites {
		if t.Failures > 0 || t.Errors > 0 {
			PrintFunc("FAILED %s\n", t.Name)
		}
		totalTestCases += len(t.TestCases)
		for _, tc := range t.TestCases {
			totalTestSteps += len(tc.TestSteps)
		}
	}

	PrintFunc("Total:%d TotalOK:%d TotalKO:%d TotalSkipped:%d TotalTestSuite:%d TotalTestCase:%d TotalTestStep:%d Duration:%s\n",
		tests.Total,
		tests.TotalOK,
		tests.TotalKO,
		tests.TotalSkipped,
		len(tests.TestSuites),
		totalTestCases,
		totalTestSteps,
		elapsed,
	)
}
<|endoftext|>


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

package json

import "bytes"

// Compact appends to dst the JSON-encoded src with
// insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error {
	return compact(dst, src, false)
}

func compact(dst *bytes.Buffer, src []byte, escape bool) error {
	origLen := dst.Len()
	var scan scanner
	scan.reset()
	start := 0
	for i, c := range src {
		if escape && (c == '<' || c == '>' || c == '&') {
			if start < i {
				dst.Write(src[start:i])
			}
			dst.WriteString(`\u00`)
			dst.WriteByte(hex[c>>4])
			dst.WriteByte(hex[c&0xF])
			start = i + 1
		}
		// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
		if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
			if start < i {
				dst.Write(src[start:i])
			}
			dst.WriteString(`\u202`)
			dst.WriteByte(hex[src[i+2]&0xF])
			start = i + 3
		}
		v := scan.step(&scan, c)
		if v >= scanSkipSpace {
			if v == scanError {
				break
			}
			if start < i {
				dst.Write(src[start:i])
			}
			start = i + 1
		}
	}
	if scan.eof() == scanError {
		dst.Truncate(origLen)
		return scan.err
	}
	if start < len(src) {
		dst.Write(src[start:])
	}
	return nil
}

func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
	dst.WriteByte('\n')
	dst.WriteString(prefix)
	for i := 0; i < depth; i++ {
		dst.WriteString(indent)
	}
}

// Indent appends to dst an indented form of the JSON-encoded src.
// Each element in a JSON object or array begins on a new,
// indented line beginning with prefix followed by one or more
// copies of indent according to the indentation nesting.
// The data appended to dst does not begin with the prefix nor
// any indentation, and has no trailing newline, to make it
// easier to embed inside other formatted JSON data.
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
	origLen := dst.Len()
	var scan scanner
	scan.reset()
	needIndent := false
	depth := 0
	for _, c := range src {
		scan.bytes++
		v := scan.step(&scan, c)
		if v == scanSkipSpace {
			continue
		}
		if v == scanError {
			break
		}
		if needIndent && v != scanEndObject && v != scanEndArray {
			needIndent = false
			depth++
			newline(dst, prefix, indent, depth)
		}

		// Emit semantically uninteresting bytes
		// (in particular, punctuation in strings) unmodified.
		if v == scanContinue {
			dst.WriteByte(c)
			continue
		}

		// Add spacing around real punctuation.
		switch c {
		case '{', '[':
			// delay indent so that empty object and array are formatted as {} and [].
			needIndent = true
			dst.WriteByte(c)

		case ',':
			dst.WriteByte(c)
			newline(dst, prefix, indent, depth)

		case ':':
			dst.WriteByte(c)
			dst.WriteByte(' ')

		case '}', ']':
			if needIndent {
				// suppress indent in empty object/array
				needIndent = false
			} else {
				depth--
				newline(dst, prefix, indent, depth)
			}
			dst.WriteByte(c)

		default:
			dst.WriteByte(c)
		}
	}
	if scan.eof() == scanError {
		dst.Truncate(origLen)
		return scan.err
	}
	return nil
}
<commit_msg>encoding/json: document Indent's preservation of trailing spaces<commit_after>// Copyright 2010 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package json

import "bytes"

// Compact appends to dst the JSON-encoded src with
// insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error {
	return compact(dst, src, false)
}

func compact(dst *bytes.Buffer, src []byte, escape bool) error {
	origLen := dst.Len()
	var scan scanner
	scan.reset()
	start := 0
	for i, c := range src {
		if escape && (c == '<' || c == '>' || c == '&') {
			if start < i {
				dst.Write(src[start:i])
			}
			dst.WriteString(`\u00`)
			dst.WriteByte(hex[c>>4])
			dst.WriteByte(hex[c&0xF])
			start = i + 1
		}
		// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
		if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
			if start < i {
				dst.Write(src[start:i])
			}
			dst.WriteString(`\u202`)
			dst.WriteByte(hex[src[i+2]&0xF])
			start = i + 3
		}
		v := scan.step(&scan, c)
		if v >= scanSkipSpace {
			if v == scanError {
				break
			}
			if start < i {
				dst.Write(src[start:i])
			}
			start = i + 1
		}
	}
	if scan.eof() == scanError {
		dst.Truncate(origLen)
		return scan.err
	}
	if start < len(src) {
		dst.Write(src[start:])
	}
	return nil
}

func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
	dst.WriteByte('\n')
	dst.WriteString(prefix)
	for i := 0; i < depth; i++ {
		dst.WriteString(indent)
	}
}

// Indent appends to dst an indented form of the JSON-encoded src.
// Each element in a JSON object or array begins on a new,
// indented line beginning with prefix followed by one or more
// copies of indent according to the indentation nesting.
// The data appended to dst does not begin with the prefix nor
// any indentation, to make it easier to embed inside other formatted JSON data.
// Although leading space characters (space, tab, carriage return, newline)
// at the beginning of src are dropped, trailing space characters
// at the end of src are preserved and copied to dst.
// For example, if src has no trailing spaces, neither will dst;
// if src ends in a trailing newline, so will dst.
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
	origLen := dst.Len()
	var scan scanner
	scan.reset()
	needIndent := false
	depth := 0
	for _, c := range src {
		scan.bytes++
		v := scan.step(&scan, c)
		if v == scanSkipSpace {
			continue
		}
		if v == scanError {
			break
		}
		if needIndent && v != scanEndObject && v != scanEndArray {
			needIndent = false
			depth++
			newline(dst, prefix, indent, depth)
		}

		// Emit semantically uninteresting bytes
		// (in particular, punctuation in strings) unmodified.
		if v == scanContinue {
			dst.WriteByte(c)
			continue
		}

		// Add spacing around real punctuation.
		switch c {
		case '{', '[':
			// delay indent so that empty object and array are formatted as {} and [].
			needIndent = true
			dst.WriteByte(c)

		case ',':
			dst.WriteByte(c)
			newline(dst, prefix, indent, depth)

		case ':':
			dst.WriteByte(c)
			dst.WriteByte(' ')

		case '}', ']':
			if needIndent {
				// suppress indent in empty object/array
				needIndent = false
			} else {
				depth--
				newline(dst, prefix, indent, depth)
			}
			dst.WriteByte(c)

		default:
			dst.WriteByte(c)
		}
	}
	if scan.eof() == scanError {
		dst.Truncate(origLen)
		return scan.err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 graph_test

import (
	"runtime"
	"sort"
	"sync"
	"testing"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/graph"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestTraverse(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helper
////////////////////////////////////////////////////////////////////////

// A graph.Visitor that invokes a wrapped function.
type funcVisitor struct {
	F func(context.Context, string) ([]string, error)
}

var _ graph.Visitor = &funcVisitor{}

func (fv *funcVisitor) Visit(
	ctx context.Context,
	node string) (adjacent []string, err error) {
	adjacent, err = fv.F(ctx, node)
	return
}

func sortNodes(in []string) (out sort.StringSlice) {
	out = make(sort.StringSlice, len(in))
	copy(out, in)
	sort.Sort(out)
	return
}

func indexNodes(nodes []string) (index map[string]int) {
	index = make(map[string]int)
	for i, n := range nodes {
		index[n] = i
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

const parallelism = 16

type TraverseTest struct {
	ctx context.Context
	mu  sync.Mutex

	// The roots from which we traverse.
	roots []string

	// Edges used by the default implementation of visit.
	edges map[string][]string

	// The function that will be called for visiting nodes. By default, this
	// writes to nodesVisited and reads from edges.
	visit func(context.Context, string) ([]string, error)

	// The nodes that were visited, in the order in which they were visited.
	//
	// GUARDED_BY(mu)
	nodesVisited []string
}

var _ SetUpTestSuiteInterface = &TraverseTest{}
var _ SetUpInterface = &TraverseTest{}

func init() { RegisterTestSuite(&TraverseTest{}) }

func (t *TraverseTest) SetUpTestSuite() {
	// Ensure that we get real parallelism where available.
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func (t *TraverseTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
}

func (t *TraverseTest) defaultVisit(
	ctx context.Context,
	node string) (adjacent []string, err error) {
	t.mu.Lock()
	t.nodesVisited = append(t.nodesVisited, node)
	t.mu.Unlock()

	adjacent = t.edges[node]
	return
}

func (t *TraverseTest) traverse() (err error) {
	// Choose a visit function.
	visit := t.visit
	if visit == nil {
		visit = t.defaultVisit
	}

	// Traverse.
	v := &funcVisitor{F: visit}
	err = graph.Traverse(t.ctx, parallelism, t.roots, v)

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *TraverseTest) EmptyGraph() {
	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	// Nothing should have been visited.
	ExpectEq(0, len(t.nodesVisited))
}

func (t *TraverseTest) SingleNodeConnectedComponents() {
	// Graph structure:
	//
	//     A  B  C  D
	//
	t.roots = []string{"A", "B", "C", "D"}
	t.edges = map[string][]string{}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
		))
}

func (t *TraverseTest) SimpleRootedTree() {
	// Graph structure:
	//
	//        A
	//      / |
	//     B  C
	//      / | \
	//     D  E  F
	//      / |  |
	//     G  H  I
	//           | \
	//           J  K
	//
	t.roots = []string{"A"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"C": []string{"D", "E", "F"},
		"E": []string{"G", "H"},
		"F": []string{"I"},
		"I": []string{"J", "K"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
			"F",
			"G",
			"H",
			"I",
			"J",
			"K",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	for p, successors := range t.edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[p], nodeIndex[s], "%q %q", p, s)
		}
	}
}

func (t *TraverseTest) SimpleDAG() {
	// Graph structure:
	//
	//        A
	//      /  \
	//     B    C
	//      \  /|
	//        D |
	//         \|
	//          E
	//
	t.roots = []string{"A"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D", "E"},
		"D": []string{"E"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	ExpectGt(nodeIndex["B"], nodeIndex["A"])
	ExpectGt(nodeIndex["C"], nodeIndex["A"])

	ExpectThat(
		nodeIndex["D"],
		AnyOf(GreaterThan(nodeIndex["B"]), GreaterThan(nodeIndex["C"])))

	ExpectThat(
		nodeIndex["E"],
		AnyOf(GreaterThan(nodeIndex["D"]), GreaterThan(nodeIndex["C"])))
}

func (t *TraverseTest) MultipleConnectedComponents() {
	// Graph structure:
	//
	//        A       E
	//      /  \      |\
	//     B    C     F G
	//      \  /
	//        D
	//
	t.roots = []string{"A", "E"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D"},
		"E": []string{"F", "G"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
			"F",
			"G",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	ExpectGt(nodeIndex["B"], nodeIndex["A"])
	ExpectGt(nodeIndex["C"], nodeIndex["A"])

	ExpectThat(
		nodeIndex["D"],
		AnyOf(GreaterThan(nodeIndex["B"]), GreaterThan(nodeIndex["C"])))

	ExpectGt(nodeIndex["F"], nodeIndex["E"])
	ExpectGt(nodeIndex["G"], nodeIndex["E"])
}

func (t *TraverseTest) RedundantRoots() {
	// Graph structure:
	//
	//        A
	//      /  \
	//     B    C
	//      \  /|
	//        D |
	//         \|
	//          E
	//
	t.roots = []string{"A", "D", "B"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D", "E"},
		"D": []string{"E"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	ExpectGt(nodeIndex["C"], nodeIndex["A"])

	ExpectThat(
		nodeIndex["E"],
		AnyOf(GreaterThan(nodeIndex["D"]), GreaterThan(nodeIndex["C"])))
}

func (t *TraverseTest) Cycle() {
	AssertFalse(true, "TODO")
}

func (t *TraverseTest) LargeRootedTree() {
	AssertFalse(true, "TODO")
}

func (t *TraverseTest) VisitorReturnsError() {
	AssertFalse(true, "TODO")
}
<commit_msg>TraverseTest.Cycle<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 graph_test

import (
	"runtime"
	"sort"
	"sync"
	"testing"

	"golang.org/x/net/context"

	"github.com/jacobsa/comeback/graph"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
)

func TestTraverse(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helper
////////////////////////////////////////////////////////////////////////

// A graph.Visitor that invokes a wrapped function.
type funcVisitor struct {
	F func(context.Context, string) ([]string, error)
}

var _ graph.Visitor = &funcVisitor{}

func (fv *funcVisitor) Visit(
	ctx context.Context,
	node string) (adjacent []string, err error) {
	adjacent, err = fv.F(ctx, node)
	return
}

func sortNodes(in []string) (out sort.StringSlice) {
	out = make(sort.StringSlice, len(in))
	copy(out, in)
	sort.Sort(out)
	return
}

func indexNodes(nodes []string) (index map[string]int) {
	index = make(map[string]int)
	for i, n := range nodes {
		index[n] = i
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

const parallelism = 16

type TraverseTest struct {
	ctx context.Context
	mu  sync.Mutex

	// The roots from which we traverse.
	roots []string

	// Edges used by the default implementation of visit.
	edges map[string][]string

	// The function that will be called for visiting nodes. By default, this
	// writes to nodesVisited and reads from edges.
	visit func(context.Context, string) ([]string, error)

	// The nodes that were visited, in the order in which they were visited.
	//
	// GUARDED_BY(mu)
	nodesVisited []string
}

var _ SetUpTestSuiteInterface = &TraverseTest{}
var _ SetUpInterface = &TraverseTest{}

func init() { RegisterTestSuite(&TraverseTest{}) }

func (t *TraverseTest) SetUpTestSuite() {
	// Ensure that we get real parallelism where available.
	runtime.GOMAXPROCS(runtime.NumCPU())
}

func (t *TraverseTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
}

func (t *TraverseTest) defaultVisit(
	ctx context.Context,
	node string) (adjacent []string, err error) {
	t.mu.Lock()
	t.nodesVisited = append(t.nodesVisited, node)
	t.mu.Unlock()

	adjacent = t.edges[node]
	return
}

func (t *TraverseTest) traverse() (err error) {
	// Choose a visit function.
	visit := t.visit
	if visit == nil {
		visit = t.defaultVisit
	}

	// Traverse.
	v := &funcVisitor{F: visit}
	err = graph.Traverse(t.ctx, parallelism, t.roots, v)

	return
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *TraverseTest) EmptyGraph() {
	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	// Nothing should have been visited.
	ExpectEq(0, len(t.nodesVisited))
}

func (t *TraverseTest) SingleNodeConnectedComponents() {
	// Graph structure:
	//
	//     A  B  C  D
	//
	t.roots = []string{"A", "B", "C", "D"}
	t.edges = map[string][]string{}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
		))
}

func (t *TraverseTest) SimpleRootedTree() {
	// Graph structure:
	//
	//        A
	//      / |
	//     B  C
	//      / | \
	//     D  E  F
	//      / |  |
	//     G  H  I
	//           | \
	//           J  K
	//
	t.roots = []string{"A"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"C": []string{"D", "E", "F"},
		"E": []string{"G", "H"},
		"F": []string{"I"},
		"I": []string{"J", "K"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
			"F",
			"G",
			"H",
			"I",
			"J",
			"K",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	for p, successors := range t.edges {
		for _, s := range successors {
			ExpectLt(nodeIndex[p], nodeIndex[s], "%q %q", p, s)
		}
	}
}

func (t *TraverseTest) SimpleDAG() {
	// Graph structure:
	//
	//        A
	//      /  \
	//     B    C
	//      \  /|
	//        D |
	//         \|
	//          E
	//
	t.roots = []string{"A"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D", "E"},
		"D": []string{"E"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	ExpectGt(nodeIndex["B"], nodeIndex["A"])
	ExpectGt(nodeIndex["C"], nodeIndex["A"])

	ExpectThat(
		nodeIndex["D"],
		AnyOf(GreaterThan(nodeIndex["B"]), GreaterThan(nodeIndex["C"])))

	ExpectThat(
		nodeIndex["E"],
		AnyOf(GreaterThan(nodeIndex["D"]), GreaterThan(nodeIndex["C"])))
}

func (t *TraverseTest) MultipleConnectedComponents() {
	// Graph structure:
	//
	//        A       E
	//      /  \      |\
	//     B    C     F G
	//      \  /
	//        D
	//
	t.roots = []string{"A", "E"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D"},
		"E": []string{"F", "G"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
			"F",
			"G",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	ExpectGt(nodeIndex["B"], nodeIndex["A"])
	ExpectGt(nodeIndex["C"], nodeIndex["A"])

	ExpectThat(
		nodeIndex["D"],
		AnyOf(GreaterThan(nodeIndex["B"]), GreaterThan(nodeIndex["C"])))

	ExpectGt(nodeIndex["F"], nodeIndex["E"])
	ExpectGt(nodeIndex["G"], nodeIndex["E"])
}

func (t *TraverseTest) RedundantRoots() {
	// Graph structure:
	//
	//        A
	//      /  \
	//     B    C
	//      \  /|
	//        D |
	//         \|
	//          E
	//
	t.roots = []string{"A", "D", "B"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D", "E"},
		"D": []string{"E"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
			"E",
		))

	nodeIndex := indexNodes(t.nodesVisited)
	ExpectGt(nodeIndex["C"], nodeIndex["A"])

	ExpectThat(
		nodeIndex["E"],
		AnyOf(GreaterThan(nodeIndex["D"]), GreaterThan(nodeIndex["C"])))
}

func (t *TraverseTest) Cycle() {
	// Graph structure:
	//
	//        A
	//      / ^\
	//     B  | C
	//      \ |/
	//        D
	//
	t.roots = []string{"A"}
	t.edges = map[string][]string{
		"A": []string{"B", "C"},
		"B": []string{"D"},
		"C": []string{"D"},
		"D": []string{"A"},
	}

	// Traverse.
	err := t.traverse()
	AssertEq(nil, err)

	AssertThat(
		sortNodes(t.nodesVisited),
		ElementsAre(
			"A",
			"B",
			"C",
			"D",
		))
}

func (t *TraverseTest) LargeRootedTree() {
	AssertFalse(true, "TODO")
}

func (t *TraverseTest) VisitorReturnsError() {
	AssertFalse(true, "TODO")
}
<|endoftext|>


<commit_before>package diag

import (
	"net/http"
	"strings"

	"github.com/prasannavl/mchain/hconv"

	"github.com/prasannavl/mchain"

	"github.com/prasannavl/go-gluons/log"
)

func LogLevelSwitcher(opts *LogSwitcherOpts) func(*http.ServeMux) {
	return func(mux *http.ServeMux) {
		mux.HandleFunc(opts.Path, hconv.FuncToHttp(LogLevelSwitchHandlerFunc(opts), nil))
	}
}

type LogSwitcherOpts struct {
	Path       string
	LevelParam string
	FlushParam string
}

func DefaultLogSwitcherOpts() LogSwitcherOpts {
	return LogSwitcherOpts{
		Path:       "/log",
		LevelParam: "set-level",
		FlushParam: "flush",
	}
}

func LogLevelSwitchHandlerFunc(opts *LogSwitcherOpts) mchain.HandlerFunc {
	if opts == nil {
		o := DefaultLogSwitcherOpts()
		opts = &o
	}
	f := func(w http.ResponseWriter, r *http.Request) error {
		flush := r.URL.Query().Get(opts.FlushParam)
		if flush != "" {
			log.Flush()
		}
		lvl := r.URL.Query().Get(opts.LevelParam)
		if lvl != "" {
			lvl = strings.ToLower(strings.TrimSpace(lvl))
			level := log.LogLevelFromString(lvl)
			if log.IsValidLevel(level) {
				log.SetFilter(log.GetLogger(), log.LogFilterForLevel(level))
				w.WriteHeader(http.StatusOK)
				return nil
			}
		}
		return nil
	}
	return f
}
<commit_msg>fix: auto opts on nil<commit_after>package diag

import (
	"net/http"
	"strings"

	"github.com/prasannavl/mchain/hconv"

	"github.com/prasannavl/mchain"

	"github.com/prasannavl/go-gluons/log"
)

func LogLevelSwitcher(opts *LogSwitcherOpts) func(*http.ServeMux) {
	if opts == nil {
		o := DefaultLogSwitcherOpts()
		opts = &o
	}
	return func(mux *http.ServeMux) {
		mux.HandleFunc(opts.Path, hconv.FuncToHttp(LogLevelSwitchHandlerFunc(opts), nil))
	}
}

type LogSwitcherOpts struct {
	Path       string
	LevelParam string
	FlushParam string
}

func DefaultLogSwitcherOpts() LogSwitcherOpts {
	return LogSwitcherOpts{
		Path:       "/log",
		LevelParam: "set-level",
		FlushParam: "flush",
	}
}

func LogLevelSwitchHandlerFunc(opts *LogSwitcherOpts) mchain.HandlerFunc {
	if opts == nil {
		o := DefaultLogSwitcherOpts()
		opts = &o
	}
	f := func(w http.ResponseWriter, r *http.Request) error {
		flush := r.URL.Query().Get(opts.FlushParam)
		if flush != "" {
			log.Flush()
		}
		lvl := r.URL.Query().Get(opts.LevelParam)
		if lvl != "" {
			lvl = strings.ToLower(strings.TrimSpace(lvl))
			level := log.LogLevelFromString(lvl)
			if log.IsValidLevel(level) {
				log.SetFilter(log.GetLogger(), log.LogFilterForLevel(level))
				w.WriteHeader(http.StatusOK)
				return nil
			}
		}
		return nil
	}
	return f
}
<|endoftext|>