<commit_before>package network

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math/rand"
	"net"
	"sort"
	"time"

	"github.com/NetSys/di/db"
	"github.com/NetSys/di/join"
	"github.com/NetSys/di/minion/consensus"

	log "github.com/Sirupsen/logrus"
)

const labelDir = "/minion/labels"
const containerDir = "/minion/containers"

// We store rand.Uint32() in a variable so it's easily mocked out by the unit tests.
// Nondeterminism is hard to test.
var rand32 = rand.Uint32

// A directory containers the first and seceond level of a Tree requested from the
// consensus store.
type directory map[string]map[string]string

// wakeChan collapses the various channels these functions wait on into a single channel.
// Multiple redundant pings will be coalesced into a single message.
func wakeChan(conn db.Conn, store consensus.Store) chan struct{} {
	labelWatch := store.Watch(labelDir, 10*time.Second)
	containerWatch := store.Watch(labelDir, 10*time.Second)
	trigg := conn.TriggerTick(30, db.MinionTable, db.ContainerTable, db.LabelTable,
		db.EtcdTable).C

	c := make(chan struct{}, 1)
	go func() {
		for {
			select {
			case <-labelWatch:
			case <-containerWatch:
			case <-trigg:
			}

			select {
			case c <- struct{}{}:
			default: // There's a notification in queue no need for another.
			}
		}
	}()

	return c
}

func readStoreRun(conn db.Conn, store consensus.Store) {
	wc := wakeChan(conn, store)

	// Before we dig into it, wait for etcd to fully come up.  Otherwise we'll spew a
	// bunch of useless error messages because the cluster isn't configured.
	for range wc {
		etcd := conn.SelectFromEtcd(nil)
		if len(etcd) == 1 && etcd[0].LeaderIP != "" {
			time.Sleep(30 * time.Second)
			break
		}
	}

	for range wc {
		labelDir, err := getDirectory(store, labelDir)
		containerDir, err2 := getDirectory(store, containerDir)
		if err2 != nil {
			err = err2
		}

		if err != nil {
			log.WithError(err).Warn("Failed to read from cluster store.")
			continue
		}

		conn.Transact(func(view db.Database) error {
			readContainerTransact(view, containerDir)
			readLabelTransact(view, labelDir)
			return nil
		})
	}
}

func readContainerTransact(view db.Database, dir directory) {
	minions := view.SelectFromMinion(nil)
	worker := len(minions) == 1 && minions[0].Role == db.Worker

	for _, container := range view.SelectFromContainer(nil) {
		container.IP = ""
		var labels []string
		if children, ok := dir[container.SchedID]; ok {
			json.Unmarshal([]byte(children["Labels"]), &labels)

			container.IP = children["IP"]
			ip := net.ParseIP(container.IP).To4()
			if ip != nil {
				container.Mac = fmt.Sprintf("02:00:%02x:%02x:%02x:%02x",
					ip[0], ip[1], ip[2], ip[3])
			}
		}

		if worker {
			// Masters get their labels from the policy, workers from the
			// consensus store.
			container.Labels = labels
		}

		view.Commit(container)
	}
}

func readLabelTransact(view db.Database, dir directory) {
	pairs, dbls, dirKeys := join.Join(view.SelectFromLabel(nil), dir.keys(),
		func(left, right interface{}) int {
			if left.(db.Label).Label == right.(string) {
				return 0
			}
			return 1
		})

	for _, dbl := range dbls {
		view.Remove(dbl.(db.Label))
	}

	for _, key := range dirKeys {
		pairs = append(pairs, join.Pair{L: view.InsertLabel(), R: key})
	}

	for _, pair := range pairs {
		dbl := pair.L.(db.Label)
		dbl.Label = pair.R.(string)
		dbl.IP = dir[dbl.Label]["IP"]
		_, dbl.MultiHost = dir[dbl.Label]["MultiHost"]
		view.Commit(dbl)
	}
}

func writeStoreRun(conn db.Conn, store consensus.Store) {
	for range wakeChan(conn, store) {
		leader := false
		var containers []db.Container
		conn.Transact(func(view db.Database) error {
			etcds := view.SelectFromEtcd(nil)
			leader = len(etcds) == 1 && etcds[0].Leader
			containers = view.SelectFromContainer(nil)
			return nil
		})

		if !leader {
			continue
		}

		if err := writeStoreContainers(store, containers); err != nil {
			log.WithError(err).Warning("Failed to update containers in ETCD")
		}

		writeStoreLabels(store, containers)
	}
}

func writeStoreContainers(store consensus.Store, containers []db.Container) error {
	var ids []string
	for _, container := range containers {
		if container.SchedID != "" {
			ids = append(ids, container.SchedID)
		}
	}

	store.Mkdir(containerDir)
	dir, err := getDirectory(store, containerDir)
	if err != nil {
		return err
	}

	syncDir(store, dir, containerDir, ids)
	syncIPs(store, dir, containerDir, net.IPv4(10, 0, 0, 0))
	syncLabels(store, dir, containerDir, containers)

	return nil
}

func writeStoreLabels(store consensus.Store, containers []db.Container) error {
	store.Mkdir(labelDir)
	dir, err := getDirectory(store, labelDir)
	if err != nil {
		return err
	}

	var ids []string
	for _, c := range containers {
		for _, l := range c.Labels {
			ids = append(ids, l)
		}
	}

	syncDir(store, dir, labelDir, ids)

	// Labels that point to a single container don't need IPs separate from their
	// constituent container IP.  Thus the following code marks those labels, by the
	// absence of the `MultiHost` file in the consensus store, and sets their IP to
	// whatever is found in the container table.

	// Map from each label to the containers that implement it.
	labelMap := map[string][]db.Container{}
	for _, dbc := range containers {
		for _, label := range dbc.Labels {
			labelMap[label] = append(labelMap[label], dbc)
		}
	}

	// Mark labels as MultiHost if they have more than one container.
	for label, dbcs := range labelMap {
		isMH := len(dbcs) > 1
		if _, ok := dir[label]["MultiHost"]; ok == isMH {
			continue
		}

		var err error
		path := fmt.Sprintf("%s/%s/MultiHost", labelDir, label)
		if isMH {
			dir[label]["MultiHost"] = "true"
			err = store.Set(path, "true")
		} else {
			delete(dir[label], "MultiHost")
			err = store.Delete(path)
		}

		if err != nil {
			log.WithFields(log.Fields{
				"label": label,
				"error": err,
			}).Warn("Failed to change MultiHost key.")
		}
	}

	// Set the IPs of the non-MultiHost containers.
	for label, dbcs := range labelMap {
		if len(dbcs) != 1 {
			continue
		}

		dbc := dbcs[0]
		if dbc.IP == "" || dir[dbc.IP]["IP"] == dbc.IP {
			continue
		}

		path := fmt.Sprintf("%s/%s/IP", labelDir, label)
		if err := store.Set(path, dbc.IP); err != nil {
			log.WithFields(log.Fields{
				"label": label,
				"IP":    dbc.IP,
				"error": err,
			}).Warn("Consensus store failed set label IP.")
		}
	}

	// Remove the non-MultiHost containers from `dir` to avoid confusing syncIPs.
	for label, mp := range dir {
		if _, ok := mp["MultiHost"]; !ok {
			delete(dir, label)
		}
	}

	syncIPs(store, dir, labelDir, net.IPv4(10, 1, 0, 0))
	return nil
}

func syncDir(store consensus.Store, dir directory, path string, idsArg []string) {
	_, dirKeys, ids := join.Join(dir.keys(), idsArg,
		func(left, right interface{}) int {
			if left.(string) != right.(string) {
				return -1
			}
			return 0
		})

	var etcdLog string
	for _, dirKey := range dirKeys {
		id := dirKey.(string)
		keyPath := fmt.Sprintf("%s/%s", path, id)
		err := store.Delete(keyPath)
		if err != nil {
			etcdLog = fmt.Sprintf("Failed to delete %s: %s", keyPath, err)
		}
		delete(dir, id)
	}

	for _, idElem := range ids {
		id := idElem.(string)
		if _, ok := dir[id]; ok {
			continue
		}

		key := fmt.Sprintf("%s/%s", path, id)
		if err := store.Mkdir(key); err != nil {
			etcdLog = fmt.Sprintf("Failed to create dir %s: %s", key, err)
			continue
		}
		dir[id] = map[string]string{}
	}

	// Etcd failure leads to a bunch of useless errors.  Therefore we only log once.
	if etcdLog != "" {
		log.Error(etcdLog)
	}
}

// syncIPs() takes a directory and creates an IP node for every entry that's missing one.
func syncIPs(store consensus.Store, dir directory, path string, prefixIP net.IP) {
	prefix := binary.BigEndian.Uint32(prefixIP.To4())
	mask := uint32(0xffff0000)

	var unassigned []string
	ipSet := map[uint32]struct{}{}
	for k, m := range dir {
		ip := parseIP(m["IP"], prefix, mask)
		if ip != 0 {
			ipSet[ip] = struct{}{}
		} else {
			unassigned = append(unassigned, k)
		}
	}

	// Don't assign the IP of the default gateway
	ipSet[parseIP(gatewayIP, prefix, mask)] = struct{}{}
	var etcdLog string
	for _, k := range unassigned {
		ip32 := randomIP(ipSet, prefix, mask)
		ipPath := fmt.Sprintf("%s/%s/IP", path, k)
		if ip32 == 0 {
			log.Errorf("Failed to allocate IP for %s.", k)
			store.Delete(ipPath)
			delete(dir[k], "IP")
			continue
		}

		b := make([]byte, 4)
		binary.BigEndian.PutUint32(b, ip32)

		ipStr := net.IP(b).String()
		if err := store.Set(ipPath, ipStr); err != nil {
			etcdLog = fmt.Sprintf("Failed to set key %s: %s", ipPath, err)
			continue
		}

		dir[k]["IP"] = ipStr
		ipSet[ip32] = struct{}{}
	}

	// Etcd failure leads to a bunch of useless errors.  Therefore we only log once.
	if etcdLog != "" {
		log.Error(etcdLog)
	}
}

func syncLabels(store consensus.Store, dir directory, path string,
	containers []db.Container) {

	idLabelMap := map[string][]string{}
	for _, container := range containers {
		if container.SchedID != "" {
			idLabelMap[container.SchedID] = container.Labels
		}
	}

	for id, children := range dir {
		labels := idLabelMap[id]
		if labels == nil {
			labels = []string{}
		}
		sort.Sort(sort.StringSlice(labels))

		jsByte, err := json.Marshal(labels)
		if err != nil {
			panic("Not Reached")
		}
		js := string(jsByte)

		if js == children["Labels"] {
			continue
		}

		key := fmt.Sprintf("%s/%s/Labels", path, id)
		if err := store.Set(key, js); err != nil {
			log.WithField("path", path).Error("Failed to set label key.")
			continue
		}
		dir[id]["Labels"] = js
	}
}

func getDirectory(store consensus.Store, path string) (directory, error) {
	tree, err := store.GetTree(path)
	if err != nil {
		return nil, err
	}

	dir := make(directory)
	for _, l1 := range tree.Children {
		childMap := make(map[string]string)
		for _, l2 := range l1.Children {
			childMap[l2.Key] = l2.Value
		}
		dir[l1.Key] = childMap
	}

	return dir, nil
}

func parseIP(ipStr string, prefix, mask uint32) uint32 {
	ip := net.ParseIP(ipStr).To4()
	if ip == nil {
		return 0
	}

	ip32 := binary.BigEndian.Uint32(ip)
	if ip32&mask != prefix {
		return 0
	}

	return ip32
}

// Choose a random IP address in prefix/mask that isn't in 'conflicts'.
// Returns 0 on failure.
func randomIP(conflicts map[uint32]struct{}, prefix, mask uint32) uint32 {
	for i := 0; i < 256; i++ {
		ip32 := (rand32() & ^mask) | (prefix & mask)
		if _, ok := conflicts[ip32]; !ok {
			return ip32
		}
	}

	return 0
}

func (dir directory) keys() []string {
	var keys []string
	for key := range dir {
		keys = append(keys, key)
	}
	return keys
}
<commit_msg>network: Reduce rate limit on etcd events<commit_after>package network

import (
	"encoding/binary"
	"encoding/json"
	"fmt"
	"math/rand"
	"net"
	"sort"
	"time"

	"github.com/NetSys/di/db"
	"github.com/NetSys/di/join"
	"github.com/NetSys/di/minion/consensus"

	log "github.com/Sirupsen/logrus"
)

const labelDir = "/minion/labels"
const containerDir = "/minion/containers"

// We store rand.Uint32() in a variable so it's easily mocked out by the unit tests.
// Nondeterminism is hard to test.
var rand32 = rand.Uint32

// A directory containers the first and seceond level of a Tree requested from the
// consensus store.
type directory map[string]map[string]string

// wakeChan collapses the various channels these functions wait on into a single channel.
// Multiple redundant pings will be coalesced into a single message.
func wakeChan(conn db.Conn, store consensus.Store) chan struct{} {
	labelWatch := store.Watch(labelDir, 1*time.Second)
	containerWatch := store.Watch(labelDir, 1*time.Second)
	trigg := conn.TriggerTick(30, db.MinionTable, db.ContainerTable, db.LabelTable,
		db.EtcdTable).C

	c := make(chan struct{}, 1)
	go func() {
		for {
			select {
			case <-labelWatch:
			case <-containerWatch:
			case <-trigg:
			}

			select {
			case c <- struct{}{}:
			default: // There's a notification in queue no need for another.
			}
		}
	}()

	return c
}

func readStoreRun(conn db.Conn, store consensus.Store) {
	wc := wakeChan(conn, store)

	// Before we dig into it, wait for etcd to fully come up.  Otherwise we'll spew a
	// bunch of useless error messages because the cluster isn't configured.
	for range wc {
		etcd := conn.SelectFromEtcd(nil)
		if len(etcd) == 1 && etcd[0].LeaderIP != "" {
			time.Sleep(30 * time.Second)
			break
		}
	}

	for range wc {
		labelDir, err := getDirectory(store, labelDir)
		containerDir, err2 := getDirectory(store, containerDir)
		if err2 != nil {
			err = err2
		}

		if err != nil {
			log.WithError(err).Warn("Failed to read from cluster store.")
			continue
		}

		conn.Transact(func(view db.Database) error {
			readContainerTransact(view, containerDir)
			readLabelTransact(view, labelDir)
			return nil
		})
	}
}

func readContainerTransact(view db.Database, dir directory) {
	minions := view.SelectFromMinion(nil)
	worker := len(minions) == 1 && minions[0].Role == db.Worker

	for _, container := range view.SelectFromContainer(nil) {
		container.IP = ""
		var labels []string
		if children, ok := dir[container.SchedID]; ok {
			json.Unmarshal([]byte(children["Labels"]), &labels)

			container.IP = children["IP"]
			ip := net.ParseIP(container.IP).To4()
			if ip != nil {
				container.Mac = fmt.Sprintf("02:00:%02x:%02x:%02x:%02x",
					ip[0], ip[1], ip[2], ip[3])
			}
		}

		if worker {
			// Masters get their labels from the policy, workers from the
			// consensus store.
			container.Labels = labels
		}

		view.Commit(container)
	}
}

func readLabelTransact(view db.Database, dir directory) {
	pairs, dbls, dirKeys := join.Join(view.SelectFromLabel(nil), dir.keys(),
		func(left, right interface{}) int {
			if left.(db.Label).Label == right.(string) {
				return 0
			}
			return 1
		})

	for _, dbl := range dbls {
		view.Remove(dbl.(db.Label))
	}

	for _, key := range dirKeys {
		pairs = append(pairs, join.Pair{L: view.InsertLabel(), R: key})
	}

	for _, pair := range pairs {
		dbl := pair.L.(db.Label)
		dbl.Label = pair.R.(string)
		dbl.IP = dir[dbl.Label]["IP"]
		_, dbl.MultiHost = dir[dbl.Label]["MultiHost"]
		view.Commit(dbl)
	}
}

func writeStoreRun(conn db.Conn, store consensus.Store) {
	for range wakeChan(conn, store) {
		leader := false
		var containers []db.Container
		conn.Transact(func(view db.Database) error {
			etcds := view.SelectFromEtcd(nil)
			leader = len(etcds) == 1 && etcds[0].Leader
			containers = view.SelectFromContainer(nil)
			return nil
		})

		if !leader {
			continue
		}

		if err := writeStoreContainers(store, containers); err != nil {
			log.WithError(err).Warning("Failed to update containers in ETCD")
		}

		writeStoreLabels(store, containers)
	}
}

func writeStoreContainers(store consensus.Store, containers []db.Container) error {
	var ids []string
	for _, container := range containers {
		if container.SchedID != "" {
			ids = append(ids, container.SchedID)
		}
	}

	store.Mkdir(containerDir)
	dir, err := getDirectory(store, containerDir)
	if err != nil {
		return err
	}

	syncDir(store, dir, containerDir, ids)
	syncIPs(store, dir, containerDir, net.IPv4(10, 0, 0, 0))
	syncLabels(store, dir, containerDir, containers)

	return nil
}

func writeStoreLabels(store consensus.Store, containers []db.Container) error {
	store.Mkdir(labelDir)
	dir, err := getDirectory(store, labelDir)
	if err != nil {
		return err
	}

	var ids []string
	for _, c := range containers {
		for _, l := range c.Labels {
			ids = append(ids, l)
		}
	}

	syncDir(store, dir, labelDir, ids)

	// Labels that point to a single container don't need IPs separate from their
	// constituent container IP.  Thus the following code marks those labels, by the
	// absence of the `MultiHost` file in the consensus store, and sets their IP to
	// whatever is found in the container table.

	// Map from each label to the containers that implement it.
	labelMap := map[string][]db.Container{}
	for _, dbc := range containers {
		for _, label := range dbc.Labels {
			labelMap[label] = append(labelMap[label], dbc)
		}
	}

	// Mark labels as MultiHost if they have more than one container.
	for label, dbcs := range labelMap {
		isMH := len(dbcs) > 1
		if _, ok := dir[label]["MultiHost"]; ok == isMH {
			continue
		}

		var err error
		path := fmt.Sprintf("%s/%s/MultiHost", labelDir, label)
		if isMH {
			dir[label]["MultiHost"] = "true"
			err = store.Set(path, "true")
		} else {
			delete(dir[label], "MultiHost")
			err = store.Delete(path)
		}

		if err != nil {
			log.WithFields(log.Fields{
				"label": label,
				"error": err,
			}).Warn("Failed to change MultiHost key.")
		}
	}

	// Set the IPs of the non-MultiHost containers.
	for label, dbcs := range labelMap {
		if len(dbcs) != 1 {
			continue
		}

		dbc := dbcs[0]
		if dbc.IP == "" || dir[dbc.IP]["IP"] == dbc.IP {
			continue
		}

		path := fmt.Sprintf("%s/%s/IP", labelDir, label)
		if err := store.Set(path, dbc.IP); err != nil {
			log.WithFields(log.Fields{
				"label": label,
				"IP":    dbc.IP,
				"error": err,
			}).Warn("Consensus store failed set label IP.")
		}
	}

	// Remove the non-MultiHost containers from `dir` to avoid confusing syncIPs.
	for label, mp := range dir {
		if _, ok := mp["MultiHost"]; !ok {
			delete(dir, label)
		}
	}

	syncIPs(store, dir, labelDir, net.IPv4(10, 1, 0, 0))
	return nil
}

func syncDir(store consensus.Store, dir directory, path string, idsArg []string) {
	_, dirKeys, ids := join.Join(dir.keys(), idsArg,
		func(left, right interface{}) int {
			if left.(string) != right.(string) {
				return -1
			}
			return 0
		})

	var etcdLog string
	for _, dirKey := range dirKeys {
		id := dirKey.(string)
		keyPath := fmt.Sprintf("%s/%s", path, id)
		err := store.Delete(keyPath)
		if err != nil {
			etcdLog = fmt.Sprintf("Failed to delete %s: %s", keyPath, err)
		}
		delete(dir, id)
	}

	for _, idElem := range ids {
		id := idElem.(string)
		if _, ok := dir[id]; ok {
			continue
		}

		key := fmt.Sprintf("%s/%s", path, id)
		if err := store.Mkdir(key); err != nil {
			etcdLog = fmt.Sprintf("Failed to create dir %s: %s", key, err)
			continue
		}
		dir[id] = map[string]string{}
	}

	// Etcd failure leads to a bunch of useless errors.  Therefore we only log once.
	if etcdLog != "" {
		log.Error(etcdLog)
	}
}

// syncIPs() takes a directory and creates an IP node for every entry that's missing one.
func syncIPs(store consensus.Store, dir directory, path string, prefixIP net.IP) {
	prefix := binary.BigEndian.Uint32(prefixIP.To4())
	mask := uint32(0xffff0000)

	var unassigned []string
	ipSet := map[uint32]struct{}{}
	for k, m := range dir {
		ip := parseIP(m["IP"], prefix, mask)
		if ip != 0 {
			ipSet[ip] = struct{}{}
		} else {
			unassigned = append(unassigned, k)
		}
	}

	// Don't assign the IP of the default gateway
	ipSet[parseIP(gatewayIP, prefix, mask)] = struct{}{}
	var etcdLog string
	for _, k := range unassigned {
		ip32 := randomIP(ipSet, prefix, mask)
		ipPath := fmt.Sprintf("%s/%s/IP", path, k)
		if ip32 == 0 {
			log.Errorf("Failed to allocate IP for %s.", k)
			store.Delete(ipPath)
			delete(dir[k], "IP")
			continue
		}

		b := make([]byte, 4)
		binary.BigEndian.PutUint32(b, ip32)

		ipStr := net.IP(b).String()
		if err := store.Set(ipPath, ipStr); err != nil {
			etcdLog = fmt.Sprintf("Failed to set key %s: %s", ipPath, err)
			continue
		}

		dir[k]["IP"] = ipStr
		ipSet[ip32] = struct{}{}
	}

	// Etcd failure leads to a bunch of useless errors.  Therefore we only log once.
	if etcdLog != "" {
		log.Error(etcdLog)
	}
}

func syncLabels(store consensus.Store, dir directory, path string,
	containers []db.Container) {

	idLabelMap := map[string][]string{}
	for _, container := range containers {
		if container.SchedID != "" {
			idLabelMap[container.SchedID] = container.Labels
		}
	}

	for id, children := range dir {
		labels := idLabelMap[id]
		if labels == nil {
			labels = []string{}
		}
		sort.Sort(sort.StringSlice(labels))

		jsByte, err := json.Marshal(labels)
		if err != nil {
			panic("Not Reached")
		}
		js := string(jsByte)

		if js == children["Labels"] {
			continue
		}

		key := fmt.Sprintf("%s/%s/Labels", path, id)
		if err := store.Set(key, js); err != nil {
			log.WithField("path", path).Error("Failed to set label key.")
			continue
		}
		dir[id]["Labels"] = js
	}
}

func getDirectory(store consensus.Store, path string) (directory, error) {
	tree, err := store.GetTree(path)
	if err != nil {
		return nil, err
	}

	dir := make(directory)
	for _, l1 := range tree.Children {
		childMap := make(map[string]string)
		for _, l2 := range l1.Children {
			childMap[l2.Key] = l2.Value
		}
		dir[l1.Key] = childMap
	}

	return dir, nil
}

func parseIP(ipStr string, prefix, mask uint32) uint32 {
	ip := net.ParseIP(ipStr).To4()
	if ip == nil {
		return 0
	}

	ip32 := binary.BigEndian.Uint32(ip)
	if ip32&mask != prefix {
		return 0
	}

	return ip32
}

// Choose a random IP address in prefix/mask that isn't in 'conflicts'.
// Returns 0 on failure.
func randomIP(conflicts map[uint32]struct{}, prefix, mask uint32) uint32 {
	for i := 0; i < 256; i++ {
		ip32 := (rand32() & ^mask) | (prefix & mask)
		if _, ok := conflicts[ip32]; !ok {
			return ip32
		}
	}

	return 0
}

func (dir directory) keys() []string {
	var keys []string
	for key := range dir {
		keys = append(keys, key)
	}
	return keys
}
<|endoftext|>


<commit_before>package model

import (
	"testing"

	"fmt"

	"strings"

	"github.com/stretchr/testify/assert"
)

func TestGetContainerNoTeam(t *testing.T) {
	container, err := GetContainer("", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestGetContainerNoChannel(t *testing.T) {
	container, err := GetContainer("TestTeam", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestGetContainerNoName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestGetContainerBigName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel",
		"LoremIpsumDolorSitAmetConsecteturAdipiscingElit")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf("try a smaller container name up to %d characters", MaxNameSize))
}

func TestGetContainerNotFound(t *testing.T) {
	containerName := "TestDoesNotExist"
	container, err := GetContainer("TestTeam", "TestChannel", containerName)

	assert.ObjectsAreEqual(Container{}, container)
	assert.NoError(t, err, fmt.Sprintf("Container %s not found", containerName))
}

func TestGetContainerIgnoreCase(t *testing.T) {
	containerName := "UPPERCASE_NAME"
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: containerName}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", containerName)

	assert.NoError(t, err2)
	assert.Equal(t, containerFromDB.Name, strings.ToLower(containerName))

	container.Delete()
}

func TestAddContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", "Name")

	assert.NoError(t, err2)
	assert.ObjectsAreEqual(container, containerFromDB)
}

func TestAddContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestAddContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestAddContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestAddContainerDuplicate(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.EqualError(t, err, "there is a container with the same name on this channel. Try a different one 😕")
}

func TestDeleteContainer(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "Name")
	assert.NoError(t, err)

	err2 := container.Delete()
	assert.NoError(t, err2)
}

func TestDeleteContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestDeleteContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestDeleteContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestDeleteContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Delete()
	assert.EqualError(t, err, "could not find this container on this channel. Can not delete 😕")
}

func TestUpdateContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	container.InUseBy = "me"
	err2 := container.Update()
	assert.NoError(t, err2)

	containerUpdated := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}

	assert.ObjectsAreEqual(containerUpdated.InUseBy, "me")
	assert.ObjectsAreEqual(container, containerUpdated)
	container.Delete()
}

func TestUpdateContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestUpdateContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestUpdateContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestUpdateContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Update()
	assert.EqualError(t, err, "could not find this container on this channel. Can not update 😕")
}

func TestListContainers(t *testing.T) {
	names := [4]string{"A", "B", "C", "D"}
	for _, name := range names {
		container := Container{TeamID: "TestList", ChannelID: "TestChannel", Name: name}
		err := container.Add()
		assert.NoError(t, err)
	}

	containers, err2 := GetContainers("TestList", "TestChannel")
	assert.NoError(t, err2)

	assert.Len(t, containers, len(names))
	for idx, container := range containers {
		assert.ObjectsAreEqual(container.Name, names[idx])
		container.Delete()
	}
}

func TestListContainersValidateTeamID(t *testing.T) {
	containers, err := GetContainers("", "")
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestListContainersValidateChannelID(t *testing.T) {
	containers, err := GetContainers("TestTeam", "")
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestRemoveInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.ClearInUse()
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Empty(t, containerFromDB.InUseBy)
	assert.Empty(t, containerFromDB.InUseForReason)
	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}
<commit_msg>Set in use tests<commit_after>package model

import (
	"testing"

	"fmt"

	"strings"

	"github.com/stretchr/testify/assert"
)

func TestGetContainerNoTeam(t *testing.T) {
	container, err := GetContainer("", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestGetContainerNoChannel(t *testing.T) {
	container, err := GetContainer("TestTeam", "", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestGetContainerNoName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestGetContainerBigName(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel",
		"LoremIpsumDolorSitAmetConsecteturAdipiscingElit")

	assert.ObjectsAreEqual(Container{}, container)
	assert.EqualError(t, err, fmt.Sprintf("try a smaller container name up to %d characters", MaxNameSize))
}

func TestGetContainerNotFound(t *testing.T) {
	containerName := "TestDoesNotExist"
	container, err := GetContainer("TestTeam", "TestChannel", containerName)

	assert.ObjectsAreEqual(Container{}, container)
	assert.NoError(t, err, fmt.Sprintf("Container %s not found", containerName))
}

func TestGetContainerIgnoreCase(t *testing.T) {
	containerName := "UPPERCASE_NAME"
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: containerName}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", containerName)

	assert.NoError(t, err2)
	assert.Equal(t, containerFromDB.Name, strings.ToLower(containerName))

	container.Delete()
}

func TestAddContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	containerFromDB, err2 := GetContainer("TestTeam", "TestChannel", "Name")

	assert.NoError(t, err2)
	assert.ObjectsAreEqual(container, containerFromDB)
}

func TestAddContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestAddContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestAddContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Add()
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestAddContainerDuplicate(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.EqualError(t, err, "there is a container with the same name on this channel. Try a different one 😕")
}

func TestDeleteContainer(t *testing.T) {
	container, err := GetContainer("TestTeam", "TestChannel", "Name")
	assert.NoError(t, err)

	err2 := container.Delete()
	assert.NoError(t, err2)
}

func TestDeleteContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestDeleteContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestDeleteContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Delete()
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestDeleteContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Delete()
	assert.EqualError(t, err, "could not find this container on this channel. Can not delete 😕")
}

func TestUpdateContainer(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Add()
	assert.NoError(t, err)

	container.InUseBy = "me"
	err2 := container.Update()
	assert.NoError(t, err2)

	containerUpdated := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}

	assert.ObjectsAreEqual(containerUpdated.InUseBy, "me")
	assert.ObjectsAreEqual(container, containerUpdated)
	container.Delete()
}

func TestUpdateContainerValidateTeamID(t *testing.T) {
	container := Container{TeamID: "", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
}

func TestUpdateContainerValidateChannelID(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
}

func TestUpdateContainerValidateName(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: ""}
	err := container.Update()
	assert.EqualError(t, err, "can not continue without a container name 🙄")
}

func TestUpdateContainerNotFound(t *testing.T) {
	container := Container{TeamID: "TestTeam", ChannelID: "TestChannel", Name: "Name"}
	err := container.Update()
	assert.EqualError(t, err, "could not find this container on this channel. Can not update 😕")
}

func TestListContainers(t *testing.T) {
	names := [4]string{"A", "B", "C", "D"}
	for _, name := range names {
		container := Container{TeamID: "TestList", ChannelID: "TestChannel", Name: name}
		err := container.Add()
		assert.NoError(t, err)
	}

	containers, err2 := GetContainers("TestList", "TestChannel")
	assert.NoError(t, err2)

	assert.Len(t, containers, len(names))
	for idx, container := range containers {
		assert.ObjectsAreEqual(container.Name, names[idx])
		container.Delete()
	}
}

func TestListContainersValidateTeamID(t *testing.T) {
	containers, err := GetContainers("", "")
	assert.EqualError(t, err, "can not continue without a teamID 🙄")
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestListContainersValidateChannelID(t *testing.T) {
	containers, err := GetContainers("TestTeam", "")
	assert.EqualError(t, err, "can not continue without a channelID 🙄")
	assert.ObjectsAreEqual(containers, []Container{})
}

func TestRemoveInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.ClearInUse()
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Empty(t, containerFromDB.InUseBy)
	assert.Empty(t, containerFromDB.InUseForReason)
	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}

func TestSetInUseData(t *testing.T) {
	team := "TestTeam"
	channel := "TestChannel"
	containerName := "name"
	user := "User"
	reason := "testing"

	container := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: "", InUseForReason: ""}
	err := container.Add()
	assert.NoError(t, err)

	err2 := container.SetInUse(user, reason)
	assert.NoError(t, err2)

	containerExpected := Container{TeamID: team, ChannelID: channel, Name: containerName, InUseBy: user, InUseForReason: reason}
	containerFromDB, err3 := GetContainer(team, channel, containerName)
	assert.NoError(t, err3)

	// Ignoring the difference for this fields
	containerExpected.ID = containerFromDB.ID
	containerExpected.CreatedAt = containerFromDB.CreatedAt
	containerExpected.UpdatedAt = containerFromDB.UpdatedAt
	containerExpected.DeletedAt = containerFromDB.DeletedAt

	assert.Equal(t, containerExpected, containerFromDB)
	container.Delete()
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package model

import (
	"encoding/base64"
	"io/ioutil"
	"testing"
)

func TestGetInfoForBytes(t *testing.T) {
	fakeFile := make([]byte, 1000)

	if info, err := GetInfoForBytes("file.txt", fakeFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "file.txt" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 1000 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "txt" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "text/plain; charset=utf-8" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = true for non-image file")
	}

	if info, err := GetInfoForBytes("file.png", fakeFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "file.png" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 1000 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "png" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "image/png" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if !info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = false for image")
	}

	// base 64 encoded version of handtinywhite.gif from http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
	gifFile, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=")
	if info, err := GetInfoForBytes("handtinywhite.gif", gifFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "handtinywhite.gif" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 35 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "gif" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "image/gif" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if !info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = false for static gif")
	}

	animatedGifFile, err := ioutil.ReadFile("../tests/testgif.gif")
	if err != nil {
		t.Fatalf("Failed to load testgif.gif: %v", err.Error())
	}
	if info, err := GetInfoForBytes("testgif.gif", animatedGifFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "testgif.gif" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 38689 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "gif" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "image/gif" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = true for animated gif")
	}

	if info, err := GetInfoForBytes("filewithoutextension", fakeFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "filewithoutextension" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 1000 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = true for non-image file")
	}
}
<commit_msg>Fixed TestGetInfoForBytes to work on all platforms (#3832)<commit_after>// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package model

import (
	"encoding/base64"
	"io/ioutil"
	"strings"
	"testing"
)

func TestGetInfoForBytes(t *testing.T) {
	fakeFile := make([]byte, 1000)

	if info, err := GetInfoForBytes("file.txt", fakeFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "file.txt" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 1000 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "txt" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if !strings.HasPrefix(info.MimeType, "text/plain") {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = true for non-image file")
	}

	if info, err := GetInfoForBytes("file.png", fakeFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "file.png" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 1000 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "png" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "image/png" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if !info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = false for image")
	}

	// base 64 encoded version of handtinywhite.gif from http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever
	gifFile, _ := base64.StdEncoding.DecodeString("R0lGODlhAQABAIABAP///wAAACwAAAAAAQABAAACAkQBADs=")
	if info, err := GetInfoForBytes("handtinywhite.gif", gifFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "handtinywhite.gif" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 35 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "gif" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "image/gif" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if !info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = false for static gif")
	}

	animatedGifFile, err := ioutil.ReadFile("../tests/testgif.gif")
	if err != nil {
		t.Fatalf("Failed to load testgif.gif: %v", err.Error())
	}
	if info, err := GetInfoForBytes("testgif.gif", animatedGifFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "testgif.gif" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 38689 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "gif" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "image/gif" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = true for animated gif")
	}

	if info, err := GetInfoForBytes("filewithoutextension", fakeFile); err != nil {
		t.Fatal(err)
	} else if info.Filename != "filewithoutextension" {
		t.Fatalf("Got incorrect filename: %v", info.Filename)
	} else if info.Size != 1000 {
		t.Fatalf("Got incorrect size: %v", info.Size)
	} else if info.Extension != "" {
		t.Fatalf("Got incorrect file extension: %v", info.Extension)
	} else if info.MimeType != "" {
		t.Fatalf("Got incorrect mime type: %v", info.MimeType)
	} else if info.HasPreviewImage {
		t.Fatalf("Got HasPreviewImage = true for non-image file")
	}
}
<|endoftext|>


<commit_before>package main

/*
	Wgetpipe takes a list of fully-qualified URLs over STDIN and Gets them,
	outputting the code, url and elapsed fetch time.

	scans stdin in a goro, spawning up to MAX getters at a time, which stream their
	responses over a channel back to main which formats the output
*/

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/fatih/color"
	"github.com/viki-org/dnscache"
	"golang.org/x/net/context/ctxhttp"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	MAX        int           // maximum number of outstanding HTTP get requests allowed
	SleepTime  time.Duration // Duration to sleep between GETter spawns
	ErrOnly    bool          // Quiet unless 0 == Code >= 400
	NoColor    bool          // Disable colorizing
	NoDnsCache bool          // Disable DNS caching
	Summary    bool          // Output final stats
	useBar     bool          // Use progress bar
	totalGuess int           // Guesstimate of number of GETs (useful with -bar)
	debug      bool          // Enable debugging
	timeout    time.Duration // How long each GET request may take

	OutFormat int         = log.Ldate | log.Ltime | log.Lshortfile
	DebugOut  *log.Logger = log.New(ioutil.Discard, "[DEBUG] ", OutFormat)
)

type urlCode struct {
	Url  string
	Code int
	Size int64
	Dur  time.Duration
	Err  error
}

func init() {
	flag.IntVar(&MAX, "max", 5, "Maximium in-flight GET requests at a time")
	flag.BoolVar(&ErrOnly, "errorsonly", false, "Only output errors (HTTP Codes >= 400)")
	flag.BoolVar(&NoColor, "nocolor", false, "Don't colorize the output")
	flag.BoolVar(&Summary, "stats", false, "Output stats at the end")
	flag.DurationVar(&SleepTime, "sleep", 0, "Amount of time to sleep between spawning a GETter (e.g. 1ms, 10s)")
	flag.DurationVar(&timeout, "timeout", 0, "Amount of time to allow each GET request (e.g. 30s, 5m)")
	flag.BoolVar(&debug, "debug", false, "Enable debug output")
	flag.BoolVar(&NoDnsCache, "nodnscache", false, "Disable DNS caching")
	flag.BoolVar(&useBar, "bar", false, "Use progress bar instead of printing lines, can still use -stats")
	flag.IntVar(&totalGuess, "guess", 0, "Rough guess of how many GETs will be coming for -bar to start at. It will adjust")
	flag.Parse()

	// Handle boring people
	if NoColor {
		color.NoColor = true
	}

	// Handle debug
	if debug {
		DebugOut = log.New(os.Stderr, "[DEBUG] ", OutFormat)
	}

	// Sets the default http client to use dnscache, because duh
	if NoDnsCache == false {
		res := dnscache.New(1 * time.Hour)
		http.DefaultClient.Transport = &http.Transport{
			MaxIdleConnsPerHost: 64,
			Dial: func(network string, address string) (net.Conn, error) {
				separator := strings.LastIndex(address, ":")
				ip, _ := res.FetchOneString(address[:separator])
				return net.Dial("tcp", ip+address[separator:])
			},
		}
	}
}

func main() {

	var bar *pb.ProgressBar
	getChan := make(chan string, MAX*10) // Channel to stream URLs to get
	rChan := make(chan urlCode)          // Channel to stream responses from the Gets
	doneChan := make(chan bool)          // Channel to signal a getter is done
	sigChan := make(chan os.Signal, 1)   // Channel to stream signals
	abortChan := make(chan bool)         // Channel to tell the getters to abort
	count := 0
	error4s := 0
	error5s := 0
	errors := 0

	// Set up the progress bar
	if useBar {
		bar = pb.New(totalGuess)
	}

	// Stream the signals we care about
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Signal handler
	go func() {
		<-sigChan
		DebugOut.Println("Signal seen, sending abort!")

		close(abortChan)
	}()

	// Spawn off the getters
	for g := 0; g < MAX; g++ {
		go getter(getChan, rChan, doneChan, abortChan, timeout)
	}

	// Block until all the getters are done, and then close rChan
	go func() {
		defer close(rChan)

		for c := 0; c < MAX; c++ {
			<-doneChan
			DebugOut.Printf("Done %d/%d\n", c+1, MAX)
		}
	}()

	// spawn off the scanner
	start := time.Now()
	go scanStdIn(getChan, abortChan, bar)

	if useBar {
		bar.Start()
	}
	// Collate the results
	for i := range rChan {
		count++

		if useBar {
			bar.Increment()
		}
		if i.Code == 0 {
			errors++
			if useBar {
				continue
			}
			color.Red("%d (%s) %s %s (%s)\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String(), i.Err)
		} else if i.Code < 400 {
			if ErrOnly || useBar {
				// skip
				continue
			}
			color.Green("%d (%s) %s %s\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String())
		} else if i.Code < 500 {
			error4s++
			if useBar {
				continue
			}
			color.Yellow("%d (%s) %s %s\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String())
		} else {
			error5s++
			if useBar {
				continue
			}
			color.Red("%d (%s) %s %s\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String())
		}
	}

	if useBar {
		bar.Finish()
	}
	elapsed := time.Since(start)

	if Summary {
		e := color.RedString("%d", errors)
		e4 := color.YellowString("%d", error4s)
		e5 := color.RedString("%d", error5s)
		fmt.Printf("\n\nGETs: %d\nErrors: %s\n500 Errors: %s\n400 Errors: %s\nElapsed Time: %s\n", count, e, e5, e4, elapsed.String())
	}
}

// scanStdIn takes a channel to pass inputted strings to,
// and does so until EOF, whereafter it closes the channel
func scanStdIn(getChan chan string, abortChan chan bool, bar *pb.ProgressBar) {
	defer close(getChan)

	scanner := bufio.NewScanner(os.Stdin)
	count := int64(0)
	for scanner.Scan() {
		select {
		case <-abortChan:
			DebugOut.Println("scanner abort seen!")
			return
		default:
		}
		DebugOut.Println("scanner sending...")

		getChan <- scanner.Text()
		count++
		if bar != nil {
			if bar.Total < count {
				bar.Total += 1
			}
		}

	}
	// POST: we've seen EOF
	DebugOut.Printf("EOF seen after %d lines\n", count)

}

// getter takes a receive channel, send channel, and done channel,
// running HTTP GETs for anything in the receive channel, returning
// formatted responses to the send channel, and signalling completion
// via the done channel
func getter(getChan chan string, rChan chan urlCode, doneChan chan bool, abortChan chan bool, timeout time.Duration) {
	defer func() { doneChan <- true }()

	var (
		ctx    context.Context
		cancel context.CancelFunc
	)
	c := &http.Client{}

	for {
		select {
		case <-abortChan:
			DebugOut.Println("getter abort seen!")
			return
		case url := <-getChan:
			if url == "" {
				// We assume an empty request is a closer
				// as that simplifies our for{select{}} loop
				// considerably
				DebugOut.Println("getter empty request seen!")
				return
			}
			DebugOut.Printf("getter getting %s\n", url)

			// Create the context
			if timeout > 0 {
				ctx, cancel = context.WithTimeout(context.Background(), timeout)
			} else {
				ctx, cancel = context.WithCancel(context.Background())
			}

			// GET!
			s := time.Now()
			response, err := ctxhttp.Get(ctx, c, url)
			d := time.Since(s)
			cancel()

			if err != nil {
				// We assume code 0 to be a non-HTTP error
				rChan <- urlCode{url, 0, 0, d, err}
			} else {
				response.Body.Close() // else leak
				rChan <- urlCode{url, response.StatusCode, response.ContentLength, d, nil}
			}

			if SleepTime > 0 {
				// Zzzzzzz
				time.Sleep(SleepTime)
			}
		}
	}
}

// ByteFormat returns a human-readable string representation of a byte count
func ByteFormat(num_in int64) string {
	suffix := "B"
	num := float64(num_in)
	units := []string{"", "K", "M", "G", "T", "P", "E", "Z"} // "Y" caught  below
	for _, unit := range units {
		if num < 1024.0 {
			return fmt.Sprintf("%3.1f%s%s", num, unit, suffix)
		}
		num = (num / 1024)
	}
	return fmt.Sprintf("%.1f%s%s", num, "Y", suffix)
}
<commit_msg>Adds -responsedebug to see full response bodies<commit_after>package main

/*
	Wgetpipe takes a list of fully-qualified URLs over STDIN and Gets them,
	outputting the code, url and elapsed fetch time.

	scans stdin in a goro, spawning up to MAX getters at a time, which stream their
	responses over a channel back to main which formats the output
*/

import (
	"bufio"
	"context"
	"flag"
	"fmt"
	"github.com/cheggaaa/pb"
	"github.com/fatih/color"
	"github.com/viki-org/dnscache"
	"golang.org/x/net/context/ctxhttp"
	"io/ioutil"
	"log"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

var (
	MAX           int           // maximum number of outstanding HTTP get requests allowed
	SleepTime     time.Duration // Duration to sleep between GETter spawns
	ErrOnly       bool          // Quiet unless 0 == Code >= 400
	NoColor       bool          // Disable colorizing
	NoDnsCache    bool          // Disable DNS caching
	Summary       bool          // Output final stats
	useBar        bool          // Use progress bar
	totalGuess    int           // Guesstimate of number of GETs (useful with -bar)
	debug         bool          // Enable debugging
	ResponseDebug bool          // Enable full response output if debug
	timeout       time.Duration // How long each GET request may take

	OutFormat int         = log.Ldate | log.Ltime | log.Lshortfile
	DebugOut  *log.Logger = log.New(ioutil.Discard, "[DEBUG] ", OutFormat)
)

type urlCode struct {
	Url  string
	Code int
	Size int64
	Dur  time.Duration
	Err  error
}

func init() {
	flag.IntVar(&MAX, "max", 5, "Maximium in-flight GET requests at a time")
	flag.BoolVar(&ErrOnly, "errorsonly", false, "Only output errors (HTTP Codes >= 400)")
	flag.BoolVar(&NoColor, "nocolor", false, "Don't colorize the output")
	flag.BoolVar(&Summary, "stats", false, "Output stats at the end")
	flag.DurationVar(&SleepTime, "sleep", 0, "Amount of time to sleep between spawning a GETter (e.g. 1ms, 10s)")
	flag.DurationVar(&timeout, "timeout", 0, "Amount of time to allow each GET request (e.g. 30s, 5m)")
	flag.BoolVar(&debug, "debug", false, "Enable debug output")
	flag.BoolVar(&ResponseDebug, "responsedebug", false, "Enable full response output if debugging is on")
	flag.BoolVar(&NoDnsCache, "nodnscache", false, "Disable DNS caching")
	flag.BoolVar(&useBar, "bar", false, "Use progress bar instead of printing lines, can still use -stats")
	flag.IntVar(&totalGuess, "guess", 0, "Rough guess of how many GETs will be coming for -bar to start at. It will adjust")
	flag.Parse()

	// Handle boring people
	if NoColor {
		color.NoColor = true
	}

	// Handle debug
	if debug {
		DebugOut = log.New(os.Stderr, "[DEBUG] ", OutFormat)
	}

	// Sets the default http client to use dnscache, because duh
	if NoDnsCache == false {
		res := dnscache.New(1 * time.Hour)
		http.DefaultClient.Transport = &http.Transport{
			MaxIdleConnsPerHost: 64,
			Dial: func(network string, address string) (net.Conn, error) {
				separator := strings.LastIndex(address, ":")
				ip, _ := res.FetchOneString(address[:separator])
				return net.Dial("tcp", ip+address[separator:])
			},
		}
	}
}

func main() {

	var bar *pb.ProgressBar
	getChan := make(chan string, MAX*10) // Channel to stream URLs to get
	rChan := make(chan urlCode)          // Channel to stream responses from the Gets
	doneChan := make(chan bool)          // Channel to signal a getter is done
	sigChan := make(chan os.Signal, 1)   // Channel to stream signals
	abortChan := make(chan bool)         // Channel to tell the getters to abort
	count := 0
	error4s := 0
	error5s := 0
	errors := 0

	// Set up the progress bar
	if useBar {
		bar = pb.New(totalGuess)
	}

	// Stream the signals we care about
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)

	// Signal handler
	go func() {
		<-sigChan
		DebugOut.Println("Signal seen, sending abort!")

		close(abortChan)
	}()

	// Spawn off the getters
	for g := 0; g < MAX; g++ {
		go getter(getChan, rChan, doneChan, abortChan, timeout)
	}

	// Block until all the getters are done, and then close rChan
	go func() {
		defer close(rChan)

		for c := 0; c < MAX; c++ {
			<-doneChan
			DebugOut.Printf("Done %d/%d\n", c+1, MAX)
		}
	}()

	// spawn off the scanner
	start := time.Now()
	go scanStdIn(getChan, abortChan, bar)

	if useBar {
		bar.Start()
	}
	// Collate the results
	for i := range rChan {
		count++

		if useBar {
			bar.Increment()
		}
		if i.Code == 0 {
			errors++
			if useBar {
				continue
			}
			color.Red("%d (%s) %s %s (%s)\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String(), i.Err)
		} else if i.Code < 400 {
			if ErrOnly || useBar {
				// skip
				continue
			}
			color.Green("%d (%s) %s %s\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String())
		} else if i.Code < 500 {
			error4s++
			if useBar {
				continue
			}
			color.Yellow("%d (%s) %s %s\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String())
		} else {
			error5s++
			if useBar {
				continue
			}
			color.Red("%d (%s) %s %s\n", i.Code, ByteFormat(i.Size), i.Url, i.Dur.String())
		}
	}

	if useBar {
		bar.Finish()
	}
	elapsed := time.Since(start)

	if Summary {
		e := color.RedString("%d", errors)
		e4 := color.YellowString("%d", error4s)
		e5 := color.RedString("%d", error5s)
		fmt.Printf("\n\nGETs: %d\nErrors: %s\n500 Errors: %s\n400 Errors: %s\nElapsed Time: %s\n", count, e, e5, e4, elapsed.String())
	}
}

// scanStdIn takes a channel to pass inputted strings to,
// and does so until EOF, whereafter it closes the channel
func scanStdIn(getChan chan string, abortChan chan bool, bar *pb.ProgressBar) {
	defer close(getChan)

	scanner := bufio.NewScanner(os.Stdin)
	count := int64(0)
	for scanner.Scan() {
		select {
		case <-abortChan:
			DebugOut.Println("scanner abort seen!")
			return
		default:
		}
		DebugOut.Println("scanner sending...")

		getChan <- scanner.Text()
		count++
		if bar != nil {
			if bar.Total < count {
				bar.Total += 1
			}
		}

	}
	// POST: we've seen EOF
	DebugOut.Printf("EOF seen after %d lines\n", count)

}

// getter takes a receive channel, send channel, and done channel,
// running HTTP GETs for anything in the receive channel, returning
// formatted responses to the send channel, and signalling completion
// via the done channel
func getter(getChan chan string, rChan chan urlCode, doneChan chan bool, abortChan chan bool, timeout time.Duration) {
	defer func() { doneChan <- true }()

	var (
		ctx    context.Context
		cancel context.CancelFunc
	)
	c := &http.Client{}

	for {
		select {
		case <-abortChan:
			DebugOut.Println("getter abort seen!")
			return
		case url := <-getChan:
			if url == "" {
				// We assume an empty request is a closer
				// as that simplifies our for{select{}} loop
				// considerably
				DebugOut.Println("getter empty request seen!")
				return
			}
			DebugOut.Printf("getter getting %s\n", url)

			// Create the context
			if timeout > 0 {
				ctx, cancel = context.WithTimeout(context.Background(), timeout)
			} else {
				ctx, cancel = context.WithCancel(context.Background())
			}

			// GET!
			s := time.Now()
			response, err := ctxhttp.Get(ctx, c, url)
			d := time.Since(s)
			cancel()

			if err != nil {
				// We assume code 0 to be a non-HTTP error
				rChan <- urlCode{url, 0, 0, d, err}
			} else {
				if ResponseDebug {
					b, err := ioutil.ReadAll(response.Body)
					if err != nil {
						DebugOut.Printf("Error reading response body: %s\n", err)
					} else {
						DebugOut.Printf("<-----\n%s\n----->\n", b)
					}
				}
				response.Body.Close() // else leak
				rChan <- urlCode{url, response.StatusCode, response.ContentLength, d, nil}
			}

			if SleepTime > 0 {
				// Zzzzzzz
				time.Sleep(SleepTime)
			}
		}
	}
}

// ByteFormat returns a human-readable string representation of a byte count
func ByteFormat(num_in int64) string {
	suffix := "B"
	num := float64(num_in)
	units := []string{"", "K", "M", "G", "T", "P", "E", "Z"} // "Y" caught  below
	for _, unit := range units {
		if num < 1024.0 {
			return fmt.Sprintf("%3.1f%s%s", num, unit, suffix)
		}
		num = (num / 1024)
	}
	return fmt.Sprintf("%.1f%s%s", num, "Y", suffix)
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"strings"
)

// FixityResult descibes the results of fetching a file from S3
// and verification of the file's sha256 checksum.
type FixityResult struct {

	// The generic file we're going to look at.
	// This file is sitting somewhere on S3.
	GenericFile *GenericFile

	// Does the file exist in S3?
	S3FileExists bool

	// The sha256 sum we calculated after downloading
	// the file.
	Sha256 string

	// Information about the result of this operation.
	WorkSummary *WorkSummary
}

// NewFixityResult returns a new empty FixityResult object for the specified
// GenericFile.
func NewFixityResult(gf *GenericFile) *FixityResult {
	return &FixityResult{
		GenericFile:  gf,
		S3FileExists: true,
		WorkSummary:  NewWorkSummary(),
	}
}

// BucketAndKey returns the name of the S3 bucket and key for the GenericFile.
func (result *FixityResult) BucketAndKey() (string, string, error) {
	parts := strings.Split(result.GenericFile.URI, "/")
	length := len(parts)
	if length < 4 {
		// This error is fatal, so don't retry.
		result.WorkSummary.AddError("GenericFile URI '%s' is invalid", result.GenericFile.URI)
		result.WorkSummary.Retry = false
		return "", "", fmt.Errorf(result.WorkSummary.FirstError())
	}
	bucket := parts[length-2]
	key := parts[length-1]
	return bucket, key, nil
}

// GotDigestFromPreservationFile returns true if result.Sha256 was set.
func (result *FixityResult) GotDigestFromPreservationFile() bool {
	return result.Sha256 != ""
}

// GenericFileHasDigest returns true if the underlying GenericFile
// includes a SHA256 checksum.
func (result *FixityResult) GenericFileHasDigest() bool {
	return result.FedoraSha256() != ""
}

// FedoraSha256 returns the SHA256 checksum that Fedora has on record.
func (result *FixityResult) FedoraSha256() string {
	checksum := result.GenericFile.GetChecksumByAlgorithm("sha256")
	if checksum == nil {
		return ""
	}
	return checksum.Digest
}

// FixityCheckPossible returns true if we have all the data we need to
// compare the existing checksum with the checksum of the S3 file.
func (result *FixityResult) FixityCheckPossible() bool {
	return result.GotDigestFromPreservationFile() && result.GenericFileHasDigest()
}

// Sha256Matches returns true if the sha256 sum we calculated for this
// file matches the sha256 sum recorded in Fedora.
func (result *FixityResult) Sha256Matches() (bool, error) {
	if result.FixityCheckPossible() == false {
		return false, fmt.Errorf("Fixity check is not possible because one or more checksums are not available.")
	}
	return result.FedoraSha256() == result.Sha256, nil
}
<commit_msg>Added NSQMessage and WorkItem to FixityResult<commit_after>package models

import (
	"fmt"
	"github.com/nsqio/go-nsq"
	"strings"
)

// FixityResult descibes the results of fetching a file from S3
// and verification of the file's sha256 checksum.
type FixityResult struct {
	// NSQMessage is the NSQ message being processed in this restore
	// request. Not serialized because it will change each time we
	// try to process a request.
	NSQMessage *nsq.Message `json:"-"`
	// WorkItem is the Pharos WorkItem we're processing.
	// Not serialized because the Pharos WorkItem record will be
	// more up-to-date and authoritative.
	WorkItem *WorkItem `json:"-"`
	// The generic file we're going to look at.
	// This file is sitting somewhere on S3.
	GenericFile *GenericFile
	// Does the file exist in S3?
	S3FileExists bool
	// The sha256 sum we calculated after downloading
	// the file.
	Sha256 string
	// Information about the result of this operation.
	WorkSummary *WorkSummary
}

// NewFixityResult returns a new empty FixityResult object for the specified
// GenericFile.
func NewFixityResult(gf *GenericFile) *FixityResult {
	return &FixityResult{
		GenericFile:  gf,
		S3FileExists: true,
		WorkSummary:  NewWorkSummary(),
	}
}

// BucketAndKey returns the name of the S3 bucket and key for the GenericFile.
func (result *FixityResult) BucketAndKey() (string, string, error) {
	parts := strings.Split(result.GenericFile.URI, "/")
	length := len(parts)
	if length < 4 {
		// This error is fatal, so don't retry.
		result.WorkSummary.AddError("GenericFile URI '%s' is invalid", result.GenericFile.URI)
		result.WorkSummary.Retry = false
		return "", "", fmt.Errorf(result.WorkSummary.FirstError())
	}
	bucket := parts[length-2]
	key := parts[length-1]
	return bucket, key, nil
}

// GotDigestFromPreservationFile returns true if result.Sha256 was set.
func (result *FixityResult) GotDigestFromPreservationFile() bool {
	return result.Sha256 != ""
}

// GenericFileHasDigest returns true if the underlying GenericFile
// includes a SHA256 checksum.
func (result *FixityResult) GenericFileHasDigest() bool {
	return result.FedoraSha256() != ""
}

// FedoraSha256 returns the SHA256 checksum that Fedora has on record.
func (result *FixityResult) FedoraSha256() string {
	checksum := result.GenericFile.GetChecksumByAlgorithm("sha256")
	if checksum == nil {
		return ""
	}
	return checksum.Digest
}

// FixityCheckPossible returns true if we have all the data we need to
// compare the existing checksum with the checksum of the S3 file.
func (result *FixityResult) FixityCheckPossible() bool {
	return result.GotDigestFromPreservationFile() && result.GenericFileHasDigest()
}

// Sha256Matches returns true if the sha256 sum we calculated for this
// file matches the sha256 sum recorded in Fedora.
func (result *FixityResult) Sha256Matches() (bool, error) {
	if result.FixityCheckPossible() == false {
		return false, fmt.Errorf("Fixity check is not possible because one or more checksums are not available.")
	}
	return result.FedoraSha256() == result.Sha256, nil
}
<|endoftext|>


<commit_before>package edit

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
)

type tokenPart struct {
	text      string
	completed bool
}

type candidate struct {
	text  string
	parts []tokenPart
	attr  string // Attribute used for preview
}

func newCandidate() *candidate {
	return &candidate{}
}

func (c *candidate) push(tp tokenPart) {
	c.text += tp.text
	c.parts = append(c.parts, tp)
}

type completion struct {
	start, end int // The text to complete is Editor.line[start:end]
	typ        TokenType
	candidates []*candidate
	current    int
}

func (c *completion) prev(cycle bool) {
	c.current--
	if c.current == -1 {
		if cycle {
			c.current = len(c.candidates) - 1
		} else {
			c.current++
		}
	}
}

func (c *completion) next(cycle bool) {
	c.current++
	if c.current == len(c.candidates) {
		if cycle {
			c.current = 0
		} else {
			c.current--
		}
	}
}

// Find candidates by matching against a prefix.
func prefixMatchCandidates(p string, all []string) (cands []*candidate) {
	for _, s := range all {
		if len(s) >= len(p) && s[:len(p)] == p {
			cand := newCandidate()
			cand.push(tokenPart{p, false})
			cand.push(tokenPart{s[len(p):], true})
			cands = append(cands, cand)
		}
	}
	return
}

func fileNames(dir string) (names []string, err error) {
	infos, e := ioutil.ReadDir(dir)
	if e != nil {
		err = e
		return
	}
	for _, info := range infos {
		names = append(names, info.Name())
	}
	return
}

var builtins []string

func init() {
	builtins = append(builtins, eval.BuiltinFnNames...)
	builtins = append(builtins, eval.BuiltinSpecialNames...)
}

func startCompletion(ed *Editor, k Key) *leReturn {
	c := &completion{}
	token := tokenAtDot(ed)
	node := token.Node
	var findAll func() ([]string, error)
	var makeCandidates func([]string) []*candidate

	head := token.Text

	ed.pushTip(fmt.Sprintf("current node is %T", node))
	if isFormHead(node) {
		// BUG(xiaq): When completing commands, only builtins are searched
		findAll = func() ([]string, error) {
			return builtins, nil
		}
		makeCandidates = func(all []string) (cands []*candidate) {
			return prefixMatchCandidates(head, all)
		}
	} else {
		// Assume that the token is an incomplete filename
		dir, file := path.Split(head)
		findAll = func() ([]string, error) {
			if dir == "" {
				return fileNames(".")
			}
			return fileNames(dir)
		}
		makeCandidates = func(all []string) (cands []*candidate) {
			// Make candidates out of elements that match the file component.
			for _, s := range all {
				if strings.HasPrefix(s, file) {
					cand := newCandidate()
					cand.push(tokenPart{head, false})
					cand.push(tokenPart{s[len(file):], true})
					cand.attr = defaultLsColor.determineAttr(cand.text)
					cands = append(cands, cand)
				}
			}
			return
		}
	}

	// BUG(xiaq): When completing, only plain expressions are supported
	all, err := findAll()
	if err != nil {
		ed.pushTip(err.Error())
		return nil
	}
	c.start = node.Begin()
	c.end = ed.dot
	// BUG(xiaq) When completing, completion.typ is always ItemBare
	c.typ = Bareword
	c.candidates = makeCandidates(all)
	if len(c.candidates) > 0 {
		ed.completion = c
		ed.mode = modeCompletion
	} else {
		ed.pushTip(fmt.Sprintf("No completion for %s", head))
	}
	return nil
}

var BadToken = Token{}

func tokenAtDot(ed *Editor) Token {
	if len(ed.tokens) == 0 || ed.dot > len(ed.line) {
		return BadToken
	}
	if ed.dot == len(ed.line) {
		return ed.tokens[len(ed.tokens)-1]
	}
	for _, token := range ed.tokens {
		if ed.dot < token.Node.End() {
			return token
		}
	}
	return BadToken
}

func isFormHead(n parse.Node) bool {
	if _, ok := n.(*parse.Chunk); ok {
		return true
	}
	if n, ok := n.(*parse.Primary); ok {
		if n, ok := n.Parent().(*parse.Indexed); ok {
			if n, ok := n.Parent().(*parse.Compound); ok {
				if _, ok := n.Parent().(*parse.Form); ok {
					return true
				}
			}
		}
	}
	return false
}
<commit_msg>edit: fix isFormHead<commit_after>package edit

import (
	"fmt"
	"io/ioutil"
	"path"
	"strings"

	"github.com/elves/elvish/eval"
	"github.com/elves/elvish/parse"
)

type tokenPart struct {
	text      string
	completed bool
}

type candidate struct {
	text  string
	parts []tokenPart
	attr  string // Attribute used for preview
}

func newCandidate() *candidate {
	return &candidate{}
}

func (c *candidate) push(tp tokenPart) {
	c.text += tp.text
	c.parts = append(c.parts, tp)
}

type completion struct {
	start, end int // The text to complete is Editor.line[start:end]
	typ        TokenType
	candidates []*candidate
	current    int
}

func (c *completion) prev(cycle bool) {
	c.current--
	if c.current == -1 {
		if cycle {
			c.current = len(c.candidates) - 1
		} else {
			c.current++
		}
	}
}

func (c *completion) next(cycle bool) {
	c.current++
	if c.current == len(c.candidates) {
		if cycle {
			c.current = 0
		} else {
			c.current--
		}
	}
}

// Find candidates by matching against a prefix.
func prefixMatchCandidates(p string, all []string) (cands []*candidate) {
	for _, s := range all {
		if len(s) >= len(p) && s[:len(p)] == p {
			cand := newCandidate()
			cand.push(tokenPart{p, false})
			cand.push(tokenPart{s[len(p):], true})
			cands = append(cands, cand)
		}
	}
	return
}

func fileNames(dir string) (names []string, err error) {
	infos, e := ioutil.ReadDir(dir)
	if e != nil {
		err = e
		return
	}
	for _, info := range infos {
		names = append(names, info.Name())
	}
	return
}

var builtins []string

func init() {
	builtins = append(builtins, eval.BuiltinFnNames...)
	builtins = append(builtins, eval.BuiltinSpecialNames...)
}

func startCompletion(ed *Editor, k Key) *leReturn {
	c := &completion{}
	token := tokenAtDot(ed)
	node := token.Node
	var findAll func() ([]string, error)
	var makeCandidates func([]string) []*candidate

	head := token.Text

	ed.pushTip(fmt.Sprintf("current node is %T", node))
	if isFormHead(node) {
		// BUG(xiaq): When completing commands, only builtins are searched
		findAll = func() ([]string, error) {
			return builtins, nil
		}
		makeCandidates = func(all []string) (cands []*candidate) {
			return prefixMatchCandidates(head, all)
		}
	} else {
		// Assume that the token is an incomplete filename
		dir, file := path.Split(head)
		findAll = func() ([]string, error) {
			if dir == "" {
				return fileNames(".")
			}
			return fileNames(dir)
		}
		makeCandidates = func(all []string) (cands []*candidate) {
			// Make candidates out of elements that match the file component.
			for _, s := range all {
				if strings.HasPrefix(s, file) {
					cand := newCandidate()
					cand.push(tokenPart{head, false})
					cand.push(tokenPart{s[len(file):], true})
					cand.attr = defaultLsColor.determineAttr(cand.text)
					cands = append(cands, cand)
				}
			}
			return
		}
	}

	// BUG(xiaq): When completing, only plain expressions are supported
	all, err := findAll()
	if err != nil {
		ed.pushTip(err.Error())
		return nil
	}
	c.start = node.Begin()
	c.end = ed.dot
	// BUG(xiaq) When completing, completion.typ is always ItemBare
	c.typ = Bareword
	c.candidates = makeCandidates(all)
	if len(c.candidates) > 0 {
		ed.completion = c
		ed.mode = modeCompletion
	} else {
		ed.pushTip(fmt.Sprintf("No completion for %s", head))
	}
	return nil
}

var BadToken = Token{}

func tokenAtDot(ed *Editor) Token {
	if len(ed.tokens) == 0 || ed.dot > len(ed.line) {
		return BadToken
	}
	if ed.dot == len(ed.line) {
		return ed.tokens[len(ed.tokens)-1]
	}
	for _, token := range ed.tokens {
		if ed.dot < token.Node.End() {
			return token
		}
	}
	return BadToken
}

func isFormHead(n parse.Node) bool {
	if _, ok := n.(*parse.Chunk); ok {
		return true
	}
	if n, ok := n.(*parse.Primary); ok {
		if n, ok := n.Parent().(*parse.Indexed); ok {
			if compound, ok := n.Parent().(*parse.Compound); ok {
				if form, ok := compound.Parent().(*parse.Form); ok {
					return compound == form.Head
				}
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>package main

import "fmt"

// A Word represents a coefficient of a WordPoly.
// TODO(akalin): Use uintptr instead.
type Word uint32

// The size of Word in bits.
const WORD_BITS = 32

// A WordPoly represents a polynomial with Word coefficients.
//
// The zero value for a WordPoly represents the zero polynomial.
type WordPoly struct {
	coeffs []Word
}

// Only polynomials built with the same value of N and R may be used
// together in one of the functions below.

// Builds a new WordPoly representing the zero polynomial
// mod (N, X^R - 1). R must fit into an int.
func NewWordPoly(N, R Word) *WordPoly {
	return &WordPoly{make([]Word, R)}
}

// Sets p to X^k + a mod (N, X^R - 1).
func (p *WordPoly) Set(a, k, N Word) {
	R := len(p.coeffs)
	p.coeffs[0] = a % N
	for i := 1; i < R; i++ {
		p.coeffs[i] = 0
	}
	p.coeffs[int(k%Word(R))] = 1
}

// Returns whether p has the same coefficients as q.
func (p *WordPoly) Eq(q *WordPoly) bool {
	R := len(p.coeffs)
	for i := 0; i < R; i++ {
		if p.coeffs[i] != q.coeffs[i] {
			return false
		}
	}
	return true
}

// Sets p to the product of p and q mod (N, X^R - 1). tmp must not
// alias p or q.
func (p *WordPoly) mul(q *WordPoly, N Word, tmp *WordPoly) {
	R := len(tmp.coeffs)
	for i := 0; i < R; i++ {
		tmp.coeffs[i] = 0
	}

	// Optimized and unrolled version of the following loop:
	//
	//   for i, j < R {
	//     tmp_{(i + j) % R} += (p_i * q_j) % N
	//   }
	for i := 0; i < R; i++ {
		for j := 0; j < R-i; j++ {
			k := i + j
			// TODO(akalin): Handle overflow here when we
			// change Word to uintptr.
			e := uint64(p.coeffs[i]) * uint64(q.coeffs[j])
			e %= uint64(N)
			e += uint64(tmp.coeffs[k])
			e %= uint64(N)
			tmp.coeffs[k] = Word(e)
		}
		for j := R - i; j < R; j++ {
			k := j - (R - i)
			// Duplicate of loop above.
			e := uint64(p.coeffs[i]) * uint64(q.coeffs[j])
			e %= uint64(N)
			e += uint64(tmp.coeffs[k])
			e %= uint64(N)
			tmp.coeffs[k] = Word(e)
		}
	}

	p.coeffs, tmp.coeffs = tmp.coeffs, p.coeffs
}

// Sets p to p^N mod (N, X^R - 1), where R is the size of p. N must be
// positive, and tmp1 and tmp2 must not alias each other or p.
func (p *WordPoly) Pow(N Word, tmp1, tmp2 *WordPoly) {
	R := len(p.coeffs)
	for i := 0; i < R; i++ {
		tmp1.coeffs[i] = p.coeffs[i]
	}

	// Find N's highest set bit.
	i := WORD_BITS - 1
	for ; (i >= 0) && ((N & (1 << uint(i))) == 0); i-- {
	}

	for i--; i >= 0; i-- {
		tmp1.mul(tmp1, N, tmp2)
		if (N & (1 << uint(i))) != 0 {
			tmp1.mul(p, N, tmp2)
		}
	}
	p.coeffs, tmp1.coeffs = tmp1.coeffs, p.coeffs
}

// fmt.Formatter implementation.
func (p *WordPoly) Format(f fmt.State, c rune) {
	i := len(p.coeffs) - 1
	for ; i >= 0 && p.coeffs[i] == 0; i-- {
	}

	if i < 0 {
		fmt.Fprint(f, "0")
		return
	}

	// Formats coeff*x^deg.
	formatNonZeroMonomial := func(f fmt.State, c rune, coeff, deg Word) {
		if coeff != 1 || deg == 0 {
			fmt.Fprint(f, coeff)
		}
		if deg != 0 {
			fmt.Fprint(f, "x")
			if deg > 1 {
				fmt.Fprint(f, "^", deg)
			}
		}
	}

	formatNonZeroMonomial(f, c, p.coeffs[i], Word(i))

	for i--; i >= 0; i-- {
		if p.coeffs[i] != 0 {
			fmt.Fprint(f, " + ")
			formatNonZeroMonomial(f, c, p.coeffs[i], Word(i))
		}
	}
}
<commit_msg>Avoid another modulo operation in inner loop of WordPoly.mul()<commit_after>package main

import "fmt"

// A Word represents a coefficient of a WordPoly.
// TODO(akalin): Use uintptr instead.
type Word uint32

// The size of Word in bits.
const WORD_BITS = 32

// A WordPoly represents a polynomial with Word coefficients.
//
// The zero value for a WordPoly represents the zero polynomial.
type WordPoly struct {
	coeffs []Word
}

// Only polynomials built with the same value of N and R may be used
// together in one of the functions below.

// Builds a new WordPoly representing the zero polynomial
// mod (N, X^R - 1). R must fit into an int.
func NewWordPoly(N, R Word) *WordPoly {
	return &WordPoly{make([]Word, R)}
}

// Sets p to X^k + a mod (N, X^R - 1).
func (p *WordPoly) Set(a, k, N Word) {
	R := len(p.coeffs)
	p.coeffs[0] = a % N
	for i := 1; i < R; i++ {
		p.coeffs[i] = 0
	}
	p.coeffs[int(k%Word(R))] = 1
}

// Returns whether p has the same coefficients as q.
func (p *WordPoly) Eq(q *WordPoly) bool {
	R := len(p.coeffs)
	for i := 0; i < R; i++ {
		if p.coeffs[i] != q.coeffs[i] {
			return false
		}
	}
	return true
}

// Sets p to the product of p and q mod (N, X^R - 1). tmp must not
// alias p or q.
func (p *WordPoly) mul(q *WordPoly, N Word, tmp *WordPoly) {
	R := len(tmp.coeffs)
	for i := 0; i < R; i++ {
		tmp.coeffs[i] = 0
	}

	// Optimized and unrolled version of the following loop:
	//
	//   for i, j < R {
	//     tmp_{(i + j) % R} += (p_i * q_j) % N
	//   }
	for i := 0; i < R; i++ {
		for j := 0; j < R-i; j++ {
			k := i + j
			// TODO(akalin): Handle overflow here when we
			// change Word to uintptr.
			e := uint64(p.coeffs[i]) * uint64(q.coeffs[j])
			// e <= (N - 1)^2 and tmp.coeffs[k] < (N - 1),
			// so this won't overflow.
			e += uint64(tmp.coeffs[k])
			e %= uint64(N)
			tmp.coeffs[k] = Word(e)
		}
		for j := R - i; j < R; j++ {
			k := j - (R - i)
			// Duplicate of loop above.
			e := uint64(p.coeffs[i]) * uint64(q.coeffs[j])
			e += uint64(tmp.coeffs[k])
			e %= uint64(N)
			tmp.coeffs[k] = Word(e)
		}
	}

	p.coeffs, tmp.coeffs = tmp.coeffs, p.coeffs
}

// Sets p to p^N mod (N, X^R - 1), where R is the size of p. N must be
// positive, and tmp1 and tmp2 must not alias each other or p.
func (p *WordPoly) Pow(N Word, tmp1, tmp2 *WordPoly) {
	R := len(p.coeffs)
	for i := 0; i < R; i++ {
		tmp1.coeffs[i] = p.coeffs[i]
	}

	// Find N's highest set bit.
	i := WORD_BITS - 1
	for ; (i >= 0) && ((N & (1 << uint(i))) == 0); i-- {
	}

	for i--; i >= 0; i-- {
		tmp1.mul(tmp1, N, tmp2)
		if (N & (1 << uint(i))) != 0 {
			tmp1.mul(p, N, tmp2)
		}
	}
	p.coeffs, tmp1.coeffs = tmp1.coeffs, p.coeffs
}

// fmt.Formatter implementation.
func (p *WordPoly) Format(f fmt.State, c rune) {
	i := len(p.coeffs) - 1
	for ; i >= 0 && p.coeffs[i] == 0; i-- {
	}

	if i < 0 {
		fmt.Fprint(f, "0")
		return
	}

	// Formats coeff*x^deg.
	formatNonZeroMonomial := func(f fmt.State, c rune, coeff, deg Word) {
		if coeff != 1 || deg == 0 {
			fmt.Fprint(f, coeff)
		}
		if deg != 0 {
			fmt.Fprint(f, "x")
			if deg > 1 {
				fmt.Fprint(f, "^", deg)
			}
		}
	}

	formatNonZeroMonomial(f, c, p.coeffs[i], Word(i))

	for i--; i >= 0; i-- {
		if p.coeffs[i] != 0 {
			fmt.Fprint(f, " + ")
			formatNonZeroMonomial(f, c, p.coeffs[i], Word(i))
		}
	}
}
<|endoftext|>


<commit_before>package txsizes_test

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

	"github.com/roasbeef/btcd/wire"
	. "github.com/roasbeef/btcwallet/wallet/internal/txsizes"
)

const (
	p2pkhScriptSize = P2PKHPkScriptSize
	p2shScriptSize  = 23
)

func makeInts(value int, n int) []int {
	v := make([]int, n)
	for i := range v {
		v[i] = value
	}
	return v
}

func TestEstimateSerializeSize(t *testing.T) {
	tests := []struct {
		InputCount           int
		OutputScriptLengths  []int
		AddChangeOutput      bool
		ExpectedSizeEstimate int
	}{
		0: {1, []int{}, false, 159},
		1: {1, []int{p2pkhScriptSize}, false, 193},
		2: {1, []int{}, true, 193},
		3: {1, []int{p2pkhScriptSize}, true, 227},
		4: {1, []int{p2shScriptSize}, false, 191},
		5: {1, []int{p2shScriptSize}, true, 225},

		6:  {2, []int{}, false, 308},
		7:  {2, []int{p2pkhScriptSize}, false, 342},
		8:  {2, []int{}, true, 342},
		9:  {2, []int{p2pkhScriptSize}, true, 376},
		10: {2, []int{p2shScriptSize}, false, 340},
		11: {2, []int{p2shScriptSize}, true, 374},

		// 0xfd is discriminant for 16-bit compact ints, compact int
		// total size increases from 1 byte to 3.
		12: {1, makeInts(p2pkhScriptSize, 0xfc), false, 8727},
		13: {1, makeInts(p2pkhScriptSize, 0xfd), false, 8727 + P2PKHOutputSize + 2},
		14: {1, makeInts(p2pkhScriptSize, 0xfc), true, 8727 + P2PKHOutputSize + 2},
		15: {0xfc, []int{}, false, 37558},
		16: {0xfd, []int{}, false, 37558 + RedeemP2PKHInputSize + 2},
	}
	for i, test := range tests {
		outputs := make([]*wire.TxOut, 0, len(test.OutputScriptLengths))
		for _, l := range test.OutputScriptLengths {
			outputs = append(outputs, &wire.TxOut{PkScript: make([]byte, l)})
		}
		actualEstimate := EstimateSerializeSize(test.InputCount, outputs, test.AddChangeOutput)
		if actualEstimate != test.ExpectedSizeEstimate {
			t.Errorf("Test %d: Got %v: Expected %v", i, actualEstimate, test.ExpectedSizeEstimate)
		}
	}
}

func TestEstimateVirtualSize(t *testing.T) {

	type estimateVSizeTest struct {
		tx             func() (*wire.MsgTx, error)
		p2wkhIns       int
		nestedP2wkhIns int
		change         bool
		result         int
	}

	// TODO(halseth): add tests for more combination out inputs/outputs.
	tests := []estimateVSizeTest{
		// Spending P2WPKH to two outputs. Example adapted from example in BIP-143.
		{
			tx: func() (*wire.MsgTx, error) {
				txHex := "01000000000101ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				return tx, nil
			},
			p2wkhIns: 1,
			result:   147,
		},
		{
			// Spending P2SH-P2WPKH to two outputs. Example adapted from example in BIP-143.
			tx: func() (*wire.MsgTx, error) {
				txHex := "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				return tx, nil
			},
			nestedP2wkhIns: 1,
			result:         170,
		},
		{
			// Spendin P2WPKH to on output, adding one change output. We reuse
			// the transaction spending to two outputs, removing one of them.
			tx: func() (*wire.MsgTx, error) {
				txHex := "01000000000101ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				// Only keep the first output.
				tx.TxOut = []*wire.TxOut{tx.TxOut[0]}
				return tx, nil
			},
			p2wkhIns: 1,
			change:   true,
			result:   144,
		},
	}

	for _, test := range tests {
		tx, err := test.tx()
		if err != nil {
			t.Fatalf("unable to get test tx: %v", err)
		}

		est := EstimateVirtualSize(0, test.p2wkhIns,
			test.nestedP2wkhIns, tx.TxOut, test.change)

		if est != test.result {
			t.Fatalf("expected estimated vsize to be %d, "+
				"instead got %d", test.result, est)
		}
	}
}
<commit_msg>wallet/size test: add vsize test for non-segwit tx<commit_after>package txsizes_test

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

	"github.com/roasbeef/btcd/wire"
	. "github.com/roasbeef/btcwallet/wallet/internal/txsizes"
)

const (
	p2pkhScriptSize = P2PKHPkScriptSize
	p2shScriptSize  = 23
)

func makeInts(value int, n int) []int {
	v := make([]int, n)
	for i := range v {
		v[i] = value
	}
	return v
}

func TestEstimateSerializeSize(t *testing.T) {
	tests := []struct {
		InputCount           int
		OutputScriptLengths  []int
		AddChangeOutput      bool
		ExpectedSizeEstimate int
	}{
		0: {1, []int{}, false, 159},
		1: {1, []int{p2pkhScriptSize}, false, 193},
		2: {1, []int{}, true, 193},
		3: {1, []int{p2pkhScriptSize}, true, 227},
		4: {1, []int{p2shScriptSize}, false, 191},
		5: {1, []int{p2shScriptSize}, true, 225},

		6:  {2, []int{}, false, 308},
		7:  {2, []int{p2pkhScriptSize}, false, 342},
		8:  {2, []int{}, true, 342},
		9:  {2, []int{p2pkhScriptSize}, true, 376},
		10: {2, []int{p2shScriptSize}, false, 340},
		11: {2, []int{p2shScriptSize}, true, 374},

		// 0xfd is discriminant for 16-bit compact ints, compact int
		// total size increases from 1 byte to 3.
		12: {1, makeInts(p2pkhScriptSize, 0xfc), false, 8727},
		13: {1, makeInts(p2pkhScriptSize, 0xfd), false, 8727 + P2PKHOutputSize + 2},
		14: {1, makeInts(p2pkhScriptSize, 0xfc), true, 8727 + P2PKHOutputSize + 2},
		15: {0xfc, []int{}, false, 37558},
		16: {0xfd, []int{}, false, 37558 + RedeemP2PKHInputSize + 2},
	}
	for i, test := range tests {
		outputs := make([]*wire.TxOut, 0, len(test.OutputScriptLengths))
		for _, l := range test.OutputScriptLengths {
			outputs = append(outputs, &wire.TxOut{PkScript: make([]byte, l)})
		}
		actualEstimate := EstimateSerializeSize(test.InputCount, outputs, test.AddChangeOutput)
		if actualEstimate != test.ExpectedSizeEstimate {
			t.Errorf("Test %d: Got %v: Expected %v", i, actualEstimate, test.ExpectedSizeEstimate)
		}
	}
}

func TestEstimateVirtualSize(t *testing.T) {

	type estimateVSizeTest struct {
		tx              func() (*wire.MsgTx, error)
		p2wpkhIns       int
		nestedp2wpkhIns int
		p2pkhIns        int
		change          bool
		result          int
	}

	// TODO(halseth): add tests for more combination out inputs/outputs.
	tests := []estimateVSizeTest{
		// Spending P2WPKH to two outputs. Example adapted from example in BIP-143.
		{
			tx: func() (*wire.MsgTx, error) {
				txHex := "01000000000101ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				return tx, nil
			},
			p2wpkhIns: 1,
			result:    147,
		},
		{
			// Spending P2SH-P2WPKH to two outputs. Example adapted from example in BIP-143.
			tx: func() (*wire.MsgTx, error) {
				txHex := "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				return tx, nil
			},
			nestedp2wpkhIns: 1,
			result:          170,
		},
		{
			// Spendin P2WPKH to on output, adding one change output. We reuse
			// the transaction spending to two outputs, removing one of them.
			tx: func() (*wire.MsgTx, error) {
				txHex := "01000000000101ef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac0247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				// Only keep the first output.
				tx.TxOut = []*wire.TxOut{tx.TxOut[0]}
				return tx, nil
			},
			p2wpkhIns: 1,
			change:    true,
			result:    144,
		},
		{
			// Spending one P2PKH to two P2PKH outputs (no witness data).
			tx: func() (*wire.MsgTx, error) {
				txHex := "0100000001a4c91c9720157a5ee582a7966471d9c70d0a860fa7757b4c42a535a12054a4c9000000006c493046022100d49c452a00e5b1213ac84d92269510a05a584a4d0949bd7d0ad4e3408ac8e80a022100bf98707ffaf1eb9dff146f7da54e68651c0a27e3653ec3882b7a95202328579c01210332d98672a4246fe917b9c724c339e757d46b1ffde3fb27fdc680b4bb29b6ad59ffffffff02a0860100000000001976a9144fb55ee0524076acd4c14e7773561e4c298c8e2788ac20688a0b000000001976a914cb7f6bb8e95a2cd06423932cfbbce73d16a18df088ac00000000"
				b, err := hex.DecodeString(txHex)
				if err != nil {
					return nil, err
				}
				tx := &wire.MsgTx{}
				err = tx.Deserialize(bytes.NewReader(b))
				if err != nil {
					return nil, err
				}

				return tx, nil
			},
			p2pkhIns: 1,
			result:   227,
		},
	}

	for _, test := range tests {
		tx, err := test.tx()
		if err != nil {
			t.Fatalf("unable to get test tx: %v", err)
		}

		est := EstimateVirtualSize(test.p2pkhIns, test.p2wpkhIns,
			test.nestedp2wpkhIns, tx.TxOut, test.change)

		if est != test.result {
			t.Fatalf("expected estimated vsize to be %d, "+
				"instead got %d", test.result, est)
		}
	}
}
<|endoftext|>


<commit_before>// +build functional

package cri_containerd

import (
	"context"
	"net"
	"testing"
	"time"

	_ "github.com/Microsoft/hcsshim/test/functional/manifest"
	"google.golang.org/grpc"
	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

const (
	daemonAddress                = "tcp://127.0.0.1:2376"
	connectTimeout               = time.Second * 10
	testNamespace                = "cri-containerd-test"
	wcowProcessRuntimeHandler    = "default-debug"
	wcowHypervisorRuntimeHandler = "wcow-debug"
	lcowRuntimeHandler           = "lcow-debug"
	imageWindowsRS5Nanoserver    = "mcr.microsoft.com/windows/nanoserver:1809"
	imageWindowsRS5Servercore    = "mcr.microsoft.com/windows/servercore:1809"
	imageLcowK8sPause            = "k8s.gcr.io/pause:3.1"
	imageLcowAlpine              = "docker.io/library/alpine:latest"
)

func newTestRuntimeClient(t *testing.T) runtime.RuntimeServiceClient {
	ctx, cancel := context.WithTimeout(context.Background(), connectTimeout)
	defer cancel()
	conn, err := grpc.DialContext(ctx, daemonAddress, grpc.WithInsecure(), grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
		return net.DialTimeout("tcp", "127.0.0.1:2376", timeout)
	}))
	if err != nil {
		t.Fatalf("failed to dial runtime client: %v", err)
	}
	return runtime.NewRuntimeServiceClient(conn)
}

func newTestImageClient(t *testing.T) runtime.ImageServiceClient {
	ctx, cancel := context.WithTimeout(context.Background(), connectTimeout)
	defer cancel()
	conn, err := grpc.DialContext(ctx, daemonAddress, grpc.WithInsecure(), grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
		return net.DialTimeout("tcp", "127.0.0.1:2376", timeout)
	}))
	if err != nil {
		t.Fatalf("failed to dial runtime client: %v", err)
	}
	return runtime.NewImageServiceClient(conn)
}

func pullRequiredImages(t *testing.T, images []string) {
	pullRequiredImagesWithLabels(t, images, map[string]string{
		"sandbox-platform": "windows/amd64", // Not required for Windows but makes the test safer depending on defaults in the config.
	})
}

func pullRequiredLcowImages(t *testing.T, images []string) {
	pullRequiredImagesWithLabels(t, images, map[string]string{
		"sandbox-platform": "linux/amd64",
	})
}

func pullRequiredImagesWithLabels(t *testing.T, images []string, labels map[string]string) {
	if len(images) < 1 {
		return
	}

	client := newTestImageClient(t)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sb := &runtime.PodSandboxConfig{
		Labels: labels,
	}
	for _, image := range images {
		_, err := client.PullImage(ctx, &runtime.PullImageRequest{
			Image: &runtime.ImageSpec{
				Image: image,
			},
			SandboxConfig: sb,
		})
		if err != nil {
			t.Fatalf("failed PullImage for image: %s, with error: %v", image, err)
		}
	}
}
<commit_msg>Update tests to new CRI runtime names<commit_after>// +build functional

package cri_containerd

import (
	"context"
	"net"
	"testing"
	"time"

	_ "github.com/Microsoft/hcsshim/test/functional/manifest"
	"google.golang.org/grpc"
	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

const (
	daemonAddress                = "tcp://127.0.0.1:2376"
	connectTimeout               = time.Second * 10
	testNamespace                = "cri-containerd-test"
	wcowProcessRuntimeHandler    = "runhcs-wcow-process"
	wcowHypervisorRuntimeHandler = "runhcs-wcow-hypervisor"
	lcowRuntimeHandler           = "runhcs-lcow"
	imageWindowsRS5Nanoserver    = "mcr.microsoft.com/windows/nanoserver:1809"
	imageWindowsRS5Servercore    = "mcr.microsoft.com/windows/servercore:1809"
	imageLcowK8sPause            = "k8s.gcr.io/pause:3.1"
	imageLcowAlpine              = "docker.io/library/alpine:latest"
)

func newTestRuntimeClient(t *testing.T) runtime.RuntimeServiceClient {
	ctx, cancel := context.WithTimeout(context.Background(), connectTimeout)
	defer cancel()
	conn, err := grpc.DialContext(ctx, daemonAddress, grpc.WithInsecure(), grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
		return net.DialTimeout("tcp", "127.0.0.1:2376", timeout)
	}))
	if err != nil {
		t.Fatalf("failed to dial runtime client: %v", err)
	}
	return runtime.NewRuntimeServiceClient(conn)
}

func newTestImageClient(t *testing.T) runtime.ImageServiceClient {
	ctx, cancel := context.WithTimeout(context.Background(), connectTimeout)
	defer cancel()
	conn, err := grpc.DialContext(ctx, daemonAddress, grpc.WithInsecure(), grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) {
		return net.DialTimeout("tcp", "127.0.0.1:2376", timeout)
	}))
	if err != nil {
		t.Fatalf("failed to dial runtime client: %v", err)
	}
	return runtime.NewImageServiceClient(conn)
}

func pullRequiredImages(t *testing.T, images []string) {
	pullRequiredImagesWithLabels(t, images, map[string]string{
		"sandbox-platform": "windows/amd64", // Not required for Windows but makes the test safer depending on defaults in the config.
	})
}

func pullRequiredLcowImages(t *testing.T, images []string) {
	pullRequiredImagesWithLabels(t, images, map[string]string{
		"sandbox-platform": "linux/amd64",
	})
}

func pullRequiredImagesWithLabels(t *testing.T, images []string, labels map[string]string) {
	if len(images) < 1 {
		return
	}

	client := newTestImageClient(t)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	sb := &runtime.PodSandboxConfig{
		Labels: labels,
	}
	for _, image := range images {
		_, err := client.PullImage(ctx, &runtime.PullImageRequest{
			Image: &runtime.ImageSpec{
				Image: image,
			},
			SandboxConfig: sb,
		})
		if err != nil {
			t.Fatalf("failed PullImage for image: %s, with error: %v", image, err)
		}
	}
}
<|endoftext|>


<commit_before>package elements

import (
	"github.com/woz5999/CueDescriptionsToASCII/elements"
	"github.com/woz5999/CueDescriptionsToASCII/validation"
	"log"
	"strings"
)

// Follow ... Follow struct
type Follow struct {
	value string
}

// SetValue ... set the value for this element
func (follow Follow) SetValue(value string) {
	// strip extraneous spaces and format
	value = strings.Replace(value, " ", "")
}

// Convert ... output ASCII for this element
func (follow Follow) Convert() string {
	ret := ""
	if follow.Validate() {
		ret = Element.Trim("Followon " + follow.value + "\r\n")
	} else {
		log.Println("Failed to validate '" + follow.value + "'")
	}
	return ret
}

// Validate ... validate the value against standard Section 10.3
func (follow Follow) Validate() bool {
	return validation.ValidateTime(delay.value)
}
<commit_msg>Remove cyclical import<commit_after>package elements

import (
	"github.com/woz5999/CueDescriptionsToASCII/validation"
	"log"
	"strings"
)

// Follow ... Follow struct
type Follow struct {
	value string
}

// SetValue ... set the value for this element
func (follow Follow) SetValue(value string) {
	// strip extraneous spaces and format
	value = strings.Replace(value, " ", "")
}

// Convert ... output ASCII for this element
func (follow Follow) Convert() string {
	ret := ""
	if follow.Validate() {
		ret = Element.Trim("Followon " + follow.value + "\r\n")
	} else {
		log.Println("Failed to validate '" + follow.value + "'")
	}
	return ret
}

// Validate ... validate the value against standard Section 10.3
func (follow Follow) Validate() bool {
	return validation.ValidateTime(delay.value)
}
<|endoftext|>