<commit_before>/*
	Token stream test fixtures.
	This is a public package because it is used by tests in the `json`, `cbor`, and `obj` packages.
	It should not be seen in the imports outside of testing.
*/
package fixtures

import (
	"errors"

	. "github.com/polydawn/refmt/tok"
)

type Sequence struct {
	Title  string
	Tokens []Token
}

// An array of well-formed token sequences.
var Sequences = []Sequence{
	// Strings.
	{"empty string",
		[]Token{
			TokStr(""),
		},
	},
	{"flat string",
		[]Token{
			TokStr("value"),
		},
	},
	{"strings needing escape",
		[]Token{
			TokStr("str\nbroken\ttabbed"),
		},
	},

	// Maps.
	{"empty map",
		[]Token{
			{Type: TMapOpen, Length: 0},
			{Type: TMapClose},
		},
	},
	{"single row map",
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("key"),
			TokStr("value"),
			{Type: TMapClose},
		},
	},
	{"duo row map",
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("key"),
			TokStr("value"),
			TokStr("k2"),
			TokStr("v2"),
			{Type: TMapClose},
		},
	},
	{"duo row map alt2",
		// same as previous, but map entries in a different order -- useful to test that unmarshaller can accept that (or, reject non-canonical orders!)
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("k2"),
			TokStr("v2"),
			TokStr("key"),
			TokStr("value"),
			{Type: TMapClose},
		},
	},

	// Arrays.
	{"empty array",
		[]Token{
			{Type: TArrOpen, Length: 0},
			{Type: TArrClose},
		},
	},
	{"single entry array",
		[]Token{
			{Type: TArrOpen, Length: 1},
			TokStr("value"),
			{Type: TArrClose},
		},
	},
	{"duo entry array",
		[]Token{
			{Type: TArrOpen, Length: 2},
			TokStr("value"),
			TokStr("v2"),
			{Type: TArrClose},
		},
	},

	// Complex / mixed / nested.
	{"array nested in map as non-first and final entry",
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("k1"),
			TokStr("v1"),
			TokStr("ke"),
			{Type: TArrOpen, Length: 3},
			TokStr("oh"),
			TokStr("whee"),
			TokStr("wow"),
			{Type: TArrClose},
			{Type: TMapClose},
		},
	},
	{"array nested in map as first and non-final entry",
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("ke"),
			{Type: TArrOpen, Length: 3},
			TokStr("oh"),
			TokStr("whee"),
			TokStr("wow"),
			{Type: TArrClose},
			TokStr("k1"),
			TokStr("v1"),
			{Type: TMapClose},
		},
	},
	{"maps nested in array",
		[]Token{
			{Type: TArrOpen, Length: 3},
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			TokStr("v"),
			{Type: TMapClose},
			TokStr("whee"),
			{Type: TMapOpen, Length: 1},
			TokStr("k1"),
			TokStr("v1"),
			{Type: TMapClose},
			{Type: TArrClose},
		},
	},
	{"arrays in arrays in arrays",
		[]Token{
			{Type: TArrOpen, Length: 1},
			{Type: TArrOpen, Length: 1},
			{Type: TArrOpen, Length: 0},
			{Type: TArrClose},
			{Type: TArrClose},
			{Type: TArrClose},
		},
	},
	{"maps nested in maps",
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			{Type: TMapOpen, Length: 1},
			TokStr("k2"),
			TokStr("v2"),
			{Type: TMapClose},
			{Type: TMapClose},
		},
	},
	{"map[str][]map[str]int",
		// this one is primarily for the objmapper tests
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			{Type: TArrOpen, Length: 2},
			{Type: TMapOpen, Length: 1},
			TokStr("k2"),
			TokInt(1),
			{Type: TMapClose},
			{Type: TMapOpen, Length: 1},
			TokStr("k2"),
			TokInt(2),
			{Type: TMapClose},
			{Type: TArrClose},
			{Type: TMapClose},
		},
	},

	// Empty and null and null-at-depth.
	{"empty",
		[]Token{},
	},
	{"null",
		[]Token{
			{Type: TNull},
		},
	},
	{"null in array",
		[]Token{
			{Type: TArrOpen, Length: 1},
			{Type: TNull},
			{Type: TArrClose},
		},
	},
	{"null in map",
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			{Type: TNull},
			{Type: TMapClose},
		},
	},

	// Numbers.
	// Warning: surprisingly contentious topic.
	// CBOR can't distinguish between positive numbers and unsigned;
	// JSON can't generally distinguish much of anything from anything, and is
	// subject to disasterous issues around floating point precision.
	//
	// Commented out because they're functionally useless -- packages define their own vagueries.
	// Except some which are used in the objmapping fixtures.
	//	{"integer zero", []Token{{Type: TInt, Int: 0}}},
	{"integer one", []Token{{Type: TInt, Int: 1}}},
	//	{"integer neg one", []Token{{Type: TInt, Int: -1}}},

	// Byte strings.
	// Warning: contentious topic.
	// JSON can't clearly represent binary types, and must use string transforms.
	{"short byte array",
		[]Token{
			{Type: TBytes, Bytes: []byte(`value`)}, // Note 'Length' field not used; would be redundant.
		},
	},
	{"long zero byte array",
		[]Token{
			{Type: TBytes, Bytes: make([]byte, 400)},
		},
	},
}

// Returns a copy of the sequence with all length info at the start of maps and arrays stripped.
// Use this when testing e.g. json and cbor-in-stream-mode, which doesn't know lengths.
func (s Sequence) SansLengthInfo() Sequence {
	v := Sequence{s.Title, make([]Token, len(s.Tokens))}
	copy(v.Tokens, s.Tokens)
	for i := range v.Tokens {
		v.Tokens[i].Length = -1
	}
	return v
}

// Sequences indexed by title.
var SequenceMap map[string]Sequence

func init() {
	SequenceMap = make(map[string]Sequence, len(Sequences))
	for _, v := range Sequences {
		SequenceMap[v.Title] = v
	}
}

// Labels for which way a token sequence fixture is malformed.
type Malformed error

var (
	MalformedUnterminatedArray Malformed = errors.New("MalformedUnterminatedArray")
	MalformedUnterminatedMap   Malformed = errors.New("MalformedUnterminatedMap")
	MalformedNilMapKey         Malformed = errors.New("MalformedNilMapKey")
	MalformedUnbalancedMap     Malformed = errors.New("MalformedUnbalancedMap")
)

// Any array of token sequences that are in some way malformed.
// TokenSinks (i.e. encoders, serializes) should be able to halt and error reasonably on these.
// It's less simple to get TokenSources to emit them, but for decoders comparable inputs should have their own test coverage.
var MalformedSequences = []struct {
	Title     string
	Seq       []Token
	Malformed Malformed
}{}
<commit_msg>Discard fixtures.MalformedSequences table; turned out unuseful.<commit_after>/*
	Token stream test fixtures.
	This is a public package because it is used by tests in the `json`, `cbor`, and `obj` packages.
	It should not be seen in the imports outside of testing.
*/
package fixtures

import (
	. "github.com/polydawn/refmt/tok"
)

type Sequence struct {
	Title  string
	Tokens []Token
}

// An array of well-formed token sequences.
var Sequences = []Sequence{
	// Strings.
	{"empty string",
		[]Token{
			TokStr(""),
		},
	},
	{"flat string",
		[]Token{
			TokStr("value"),
		},
	},
	{"strings needing escape",
		[]Token{
			TokStr("str\nbroken\ttabbed"),
		},
	},

	// Maps.
	{"empty map",
		[]Token{
			{Type: TMapOpen, Length: 0},
			{Type: TMapClose},
		},
	},
	{"single row map",
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("key"),
			TokStr("value"),
			{Type: TMapClose},
		},
	},
	{"duo row map",
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("key"),
			TokStr("value"),
			TokStr("k2"),
			TokStr("v2"),
			{Type: TMapClose},
		},
	},
	{"duo row map alt2",
		// same as previous, but map entries in a different order -- useful to test that unmarshaller can accept that (or, reject non-canonical orders!)
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("k2"),
			TokStr("v2"),
			TokStr("key"),
			TokStr("value"),
			{Type: TMapClose},
		},
	},

	// Arrays.
	{"empty array",
		[]Token{
			{Type: TArrOpen, Length: 0},
			{Type: TArrClose},
		},
	},
	{"single entry array",
		[]Token{
			{Type: TArrOpen, Length: 1},
			TokStr("value"),
			{Type: TArrClose},
		},
	},
	{"duo entry array",
		[]Token{
			{Type: TArrOpen, Length: 2},
			TokStr("value"),
			TokStr("v2"),
			{Type: TArrClose},
		},
	},

	// Complex / mixed / nested.
	{"array nested in map as non-first and final entry",
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("k1"),
			TokStr("v1"),
			TokStr("ke"),
			{Type: TArrOpen, Length: 3},
			TokStr("oh"),
			TokStr("whee"),
			TokStr("wow"),
			{Type: TArrClose},
			{Type: TMapClose},
		},
	},
	{"array nested in map as first and non-final entry",
		[]Token{
			{Type: TMapOpen, Length: 2},
			TokStr("ke"),
			{Type: TArrOpen, Length: 3},
			TokStr("oh"),
			TokStr("whee"),
			TokStr("wow"),
			{Type: TArrClose},
			TokStr("k1"),
			TokStr("v1"),
			{Type: TMapClose},
		},
	},
	{"maps nested in array",
		[]Token{
			{Type: TArrOpen, Length: 3},
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			TokStr("v"),
			{Type: TMapClose},
			TokStr("whee"),
			{Type: TMapOpen, Length: 1},
			TokStr("k1"),
			TokStr("v1"),
			{Type: TMapClose},
			{Type: TArrClose},
		},
	},
	{"arrays in arrays in arrays",
		[]Token{
			{Type: TArrOpen, Length: 1},
			{Type: TArrOpen, Length: 1},
			{Type: TArrOpen, Length: 0},
			{Type: TArrClose},
			{Type: TArrClose},
			{Type: TArrClose},
		},
	},
	{"maps nested in maps",
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			{Type: TMapOpen, Length: 1},
			TokStr("k2"),
			TokStr("v2"),
			{Type: TMapClose},
			{Type: TMapClose},
		},
	},
	{"map[str][]map[str]int",
		// this one is primarily for the objmapper tests
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			{Type: TArrOpen, Length: 2},
			{Type: TMapOpen, Length: 1},
			TokStr("k2"),
			TokInt(1),
			{Type: TMapClose},
			{Type: TMapOpen, Length: 1},
			TokStr("k2"),
			TokInt(2),
			{Type: TMapClose},
			{Type: TArrClose},
			{Type: TMapClose},
		},
	},

	// Empty and null and null-at-depth.
	{"empty",
		[]Token{},
	},
	{"null",
		[]Token{
			{Type: TNull},
		},
	},
	{"null in array",
		[]Token{
			{Type: TArrOpen, Length: 1},
			{Type: TNull},
			{Type: TArrClose},
		},
	},
	{"null in map",
		[]Token{
			{Type: TMapOpen, Length: 1},
			TokStr("k"),
			{Type: TNull},
			{Type: TMapClose},
		},
	},

	// Numbers.
	// Warning: surprisingly contentious topic.
	// CBOR can't distinguish between positive numbers and unsigned;
	// JSON can't generally distinguish much of anything from anything, and is
	// subject to disasterous issues around floating point precision.
	//
	// Commented out because they're functionally useless -- packages define their own vagueries.
	// Except some which are used in the objmapping fixtures.
	//	{"integer zero", []Token{{Type: TInt, Int: 0}}},
	{"integer one", []Token{{Type: TInt, Int: 1}}},
	//	{"integer neg one", []Token{{Type: TInt, Int: -1}}},

	// Byte strings.
	// Warning: contentious topic.
	// JSON can't clearly represent binary types, and must use string transforms.
	{"short byte array",
		[]Token{
			{Type: TBytes, Bytes: []byte(`value`)}, // Note 'Length' field not used; would be redundant.
		},
	},
	{"long zero byte array",
		[]Token{
			{Type: TBytes, Bytes: make([]byte, 400)},
		},
	},
}

// Returns a copy of the sequence with all length info at the start of maps and arrays stripped.
// Use this when testing e.g. json and cbor-in-stream-mode, which doesn't know lengths.
func (s Sequence) SansLengthInfo() Sequence {
	v := Sequence{s.Title, make([]Token, len(s.Tokens))}
	copy(v.Tokens, s.Tokens)
	for i := range v.Tokens {
		v.Tokens[i].Length = -1
	}
	return v
}

// Sequences indexed by title.
var SequenceMap map[string]Sequence

func init() {
	SequenceMap = make(map[string]Sequence, len(Sequences))
	for _, v := range Sequences {
		SequenceMap[v.Title] = v
	}
}
<|endoftext|>


<commit_before>package slave

import (
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"github.com/stretchr/testify/assert"
	"golang.org/x/sys/unix"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"
)

func (p *ProcessManager) GetProcess(port msp.PortNumber) *exec.Cmd {
	return p.runningProcesses[port]
}

const dataDir = "/tmp/testdir"

func TestMain(m *testing.M) {
	if err := unix.Access(dataDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := unix.Mkdir(dataDir, 0700); err != nil {
			log.Printf("Could not create a readable and writable directory at %s: %s", dataDir, err)
			return
		}
	}

	dbDir := fmt.Sprintf("%s/%s", dataDir, DataDBDir)
	if err := unix.Access(dbDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := unix.Mkdir(dbDir, 0700); err != nil {
			log.Printf("Could not create a readable and writable directory at %s: %s", dbDir, err)
			return
		}
	}

	//If the killing does not work this test would run forever otherwise
	go func() {
		time.Sleep(5 * time.Second)
		os.Exit(1)
	}()

	ret := m.Run()

	os.RemoveAll(dataDir)

	os.Exit(ret)
}

func TestProcessManager_SpawnProcess(t *testing.T) {
	var err error

	p := NewProcessManager("./fakemongod.sh", dataDir)
	p.Run()

	err = p.SpawnProcess(msp.Mongod{
		ReplicaSetName: "replSet",
		Port:           10,
	})
	assert.NoError(t, err)

	procs := p.RunningProcesses()
	assert.Equal(t, []msp.PortNumber{10}, procs)

	cmd := p.GetProcess(10)

	if err = cmd.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}

	assert.Equal(t, []string{
		"./fakemongod.sh",
		"--dbpath",
		"/tmp/testdir/db/replSet",
		"--port",
		"10",
		"--replSet",
		"replSet",
	}, cmd.Args)

	// cleanup
	err = cmd.Process.Signal(syscall.SIGKILL)
	if err != nil {
		t.Error(err)
	}
}

func TestProcessManager_KillProcess(t *testing.T) {
	var err error

	p := NewProcessManager("./fakemongod.sh", dataDir)
	p.Run()

	err = p.SpawnProcess(msp.Mongod{
		ReplicaSetName: "replSet",
		Port:           10,
	})
	assert.NoError(t, err)

	err = p.SpawnProcess(msp.Mongod{
		ReplicaSetName: "replSet",
		Port:           11,
	})
	assert.NoError(t, err)

	cmd1 := p.GetProcess(10)
	cmd2 := p.GetProcess(11)

	if err = cmd1.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}
	if err = cmd2.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}

	procs := p.RunningProcesses()
	assert.Len(t, procs, 2)
	assert.Contains(t, procs, msp.PortNumber(10))
	assert.Contains(t, procs, msp.PortNumber(10))

	p.KillProcess(10)
	cmd1.Process.Wait() //Less racy tests

	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup
	procs = p.RunningProcesses()
	assert.Equal(t, []msp.PortNumber{11}, procs)

	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup
	if err = cmd1.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 10 still alive after killing")
	}
	if err = cmd2.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}

	cmd2.Process.Kill()
	cmd2.Process.Wait()               //Less racy tests
	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup

	procs = p.RunningProcesses()
	assert.Empty(t, procs)

	if err = cmd2.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 11 still alive after killing")
	}

	// cleanup
	cmd1.Process.Signal(syscall.SIGKILL)
	cmd2.Process.Signal(syscall.SIGKILL)
}

func TestProcessManager_KillProcesses(t *testing.T) {
	var err error

	p := NewProcessManager("./fakemongod.sh", dataDir)
	p.Run()

	err = p.SpawnProcess(msp.Mongod{
		ReplicaSetName: "replSet",
		Port:           10,
	})
	if err != nil {
		t.Error(err)
	}

	err = p.SpawnProcess(msp.Mongod{
		ReplicaSetName: "replSet",
		Port:           11,
	})
	if err != nil {
		t.Error(err)
	}

	cmd1 := p.GetProcess(10)
	cmd2 := p.GetProcess(11)

	p.KillProcesses()
	cmd1.Process.Wait() //Less racy tests
	cmd2.Process.Wait() //Less racy tests

	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup

	procs := p.RunningProcesses()
	assert.Empty(t, procs)

	if err = cmd1.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 10 still alive after killing")
	}
	if err = cmd2.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 11 still alive after killing")
	}

	// cleanup
	cmd1.Process.Signal(syscall.SIGKILL)
	cmd2.Process.Signal(syscall.SIGKILL)
}
<commit_msg>FIX: processmanager: adjust tests to change in msp<commit_after>package slave

import (
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"github.com/stretchr/testify/assert"
	"golang.org/x/sys/unix"
	"os"
	"os/exec"
	"syscall"
	"testing"
	"time"
)

func (p *ProcessManager) GetProcess(port msp.PortNumber) *exec.Cmd {
	return p.runningProcesses[port]
}

const dataDir = "/tmp/testdir"

func TestMain(m *testing.M) {
	if err := unix.Access(dataDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := unix.Mkdir(dataDir, 0700); err != nil {
			log.Printf("Could not create a readable and writable directory at %s: %s", dataDir, err)
			return
		}
	}

	dbDir := fmt.Sprintf("%s/%s", dataDir, DataDBDir)
	if err := unix.Access(dbDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := unix.Mkdir(dbDir, 0700); err != nil {
			log.Printf("Could not create a readable and writable directory at %s: %s", dbDir, err)
			return
		}
	}

	//If the killing does not work this test would run forever otherwise
	go func() {
		time.Sleep(5 * time.Second)
		os.Exit(1)
	}()

	ret := m.Run()

	os.RemoveAll(dataDir)

	os.Exit(ret)
}

func TestProcessManager_SpawnProcess(t *testing.T) {
	var err error

	p := NewProcessManager("./fakemongod.sh", dataDir)
	p.Run()

	err = p.SpawnProcess(msp.Mongod{
		Port: 10,
		ReplicaSetConfig: msp.ReplicaSetConfig{
			ReplicaSetName: "replSet",
		},
	})
	assert.NoError(t, err)

	procs := p.RunningProcesses()
	assert.Equal(t, []msp.PortNumber{10}, procs)

	cmd := p.GetProcess(10)

	if err = cmd.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}

	assert.Equal(t, []string{
		"./fakemongod.sh",
		"--dbpath",
		"/tmp/testdir/db/10:replSet",
		"--port",
		"10",
		"--replSet",
		"replSet",
	}, cmd.Args)

	// cleanup
	err = cmd.Process.Signal(syscall.SIGKILL)
	if err != nil {
		t.Error(err)
	}
}

func TestProcessManager_KillProcess(t *testing.T) {
	var err error

	p := NewProcessManager("./fakemongod.sh", dataDir)
	p.Run()

	err = p.SpawnProcess(msp.Mongod{
		Port: 10,
		ReplicaSetConfig: msp.ReplicaSetConfig{
			ReplicaSetName: "replSet",
		},
	})
	assert.NoError(t, err)

	err = p.SpawnProcess(msp.Mongod{
		Port: 11,
		ReplicaSetConfig: msp.ReplicaSetConfig{
			ReplicaSetName: "replSet",
		},
	})
	assert.NoError(t, err)

	cmd1 := p.GetProcess(10)
	cmd2 := p.GetProcess(11)

	if err = cmd1.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}
	if err = cmd2.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}

	procs := p.RunningProcesses()
	assert.Len(t, procs, 2)
	assert.Contains(t, procs, msp.PortNumber(10))
	assert.Contains(t, procs, msp.PortNumber(10))

	p.KillProcess(10)
	cmd1.Process.Wait() //Less racy tests

	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup
	procs = p.RunningProcesses()
	assert.Equal(t, []msp.PortNumber{11}, procs)

	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup
	if err = cmd1.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 10 still alive after killing")
	}
	if err = cmd2.Process.Signal(syscall.Signal(0)); err != nil {
		t.Error(err)
	}

	cmd2.Process.Kill()
	cmd2.Process.Wait()               //Less racy tests
	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup

	procs = p.RunningProcesses()
	assert.Empty(t, procs)

	if err = cmd2.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 11 still alive after killing")
	}

	// cleanup
	cmd1.Process.Signal(syscall.SIGKILL)
	cmd2.Process.Signal(syscall.SIGKILL)
}

func TestProcessManager_KillProcesses(t *testing.T) {
	var err error

	p := NewProcessManager("./fakemongod.sh", dataDir)
	p.Run()

	err = p.SpawnProcess(msp.Mongod{
		Port: 10,
		ReplicaSetConfig: msp.ReplicaSetConfig{
			ReplicaSetName: "replSet",
		},
	})
	if err != nil {
		t.Error(err)
	}

	err = p.SpawnProcess(msp.Mongod{
		Port: 11,
		ReplicaSetConfig: msp.ReplicaSetConfig{
			ReplicaSetName: "replSet",
		},
	})
	if err != nil {
		t.Error(err)
	}

	cmd1 := p.GetProcess(10)
	cmd2 := p.GetProcess(11)

	p.KillProcesses()
	cmd1.Process.Wait() //Less racy tests
	cmd2.Process.Wait() //Less racy tests

	time.Sleep(10 * time.Millisecond) // give goroutines a chance to cleanup

	procs := p.RunningProcesses()
	assert.Empty(t, procs)

	if err = cmd1.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 10 still alive after killing")
	}
	if err = cmd2.Process.Signal(syscall.Signal(0)); err == nil {
		t.Error("Process 11 still alive after killing")
	}

	// cleanup
	cmd1.Process.Signal(syscall.SIGKILL)
	cmd2.Process.Signal(syscall.SIGKILL)
}
<|endoftext|>


<commit_before>package scheduler

import (
	"reflect"
	"testing"

	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
)

func TestServiceStack_SetNodes(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewGenericStack(false, ctx)

	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack.SetNodes(nodes)

	// Check that our scan limit is updated
	if stack.limit.limit != 3 {
		t.Fatalf("bad limit %d", stack.limit.limit)
	}

	out := collectFeasible(stack.source)
	if !reflect.DeepEqual(out, nodes) {
		t.Fatalf("bad: %#v", out)
	}
}

func TestServiceStack_SetJob(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewGenericStack(false, ctx)

	job := mock.Job()
	stack.SetJob(job)

	if stack.binPack.priority != job.Priority {
		t.Fatalf("bad")
	}
	if !reflect.DeepEqual(stack.jobConstraint.constraints, job.Constraints) {
		t.Fatalf("bad")
	}
}

func TestServiceStack_Select_Size(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
	}
	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	node, size := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}
	if size == nil {
		t.Fatalf("missing size")
	}

	if size.CPU != 500 || size.MemoryMB != 256 {
		t.Fatalf("bad: %#v", size)
	}

	met := ctx.Metrics()
	if met.AllocationTime == 0 {
		t.Fatalf("missing time")
	}
}

func TestServiceStack_Select_MetricsReset(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	n1, _ := stack.Select(job.TaskGroups[0])
	m1 := ctx.Metrics()
	if n1 == nil {
		t.Fatalf("missing node %#v", m1)
	}

	if m1.NodesEvaluated != 2 {
		t.Fatalf("should only be 2")
	}

	n2, _ := stack.Select(job.TaskGroups[0])
	m2 := ctx.Metrics()
	if n2 == nil {
		t.Fatalf("missing node %#v", m2)
	}

	// If we don't reset, this would be 4
	if m2.NodesEvaluated != 2 {
		t.Fatalf("should only be 2")
	}
}

func TestServiceStack_Select_DriverFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	zero.Attributes["driver.foo"] = "1"

	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.TaskGroups[0].Tasks[0].Driver = "foo"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}
}

func TestServiceStack_Select_ConstraintFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	zero.Attributes["kernel.name"] = "freebsd"

	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.Constraints[0].RTarget = "freebsd"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesFiltered != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassFiltered["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ConstraintFiltered["$attr.kernel.name = freebsd"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
}

func TestServiceStack_Select_BinPack_Overflow(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	one := nodes[1]
	one.Reserved = one.Resources

	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesExhausted != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassExhausted["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if len(met.Scores) != 1 {
		t.Fatalf("bad: %#v", met)
	}
}

func TestSystemStack_SetNodes(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewSystemStack(ctx)

	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack.SetNodes(nodes)

	out := collectFeasible(stack.source)
	if !reflect.DeepEqual(out, nodes) {
		t.Fatalf("bad: %#v", out)
	}
}

func TestSystemStack_SetJob(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewSystemStack(ctx)

	job := mock.Job()
	stack.SetJob(job)

	if stack.binPack.priority != job.Priority {
		t.Fatalf("bad")
	}
	if !reflect.DeepEqual(stack.jobConstraint.constraints, job.Constraints) {
		t.Fatalf("bad")
	}
}

func TestSystemStack_Select_Size(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{mock.Node()}
	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	node, size := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}
	if size == nil {
		t.Fatalf("missing size")
	}

	if size.CPU != 500 || size.MemoryMB != 256 {
		t.Fatalf("bad: %#v", size)
	}

	met := ctx.Metrics()
	if met.AllocationTime == 0 {
		t.Fatalf("missing time")
	}
}

func TestSystemStack_Select_MetricsReset(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	n1, _ := stack.Select(job.TaskGroups[0])
	m1 := ctx.Metrics()
	if n1 == nil {
		t.Fatalf("missing node %#v", m1)
	}

	if m1.NodesEvaluated != 1 {
		t.Fatalf("should only be 1")
	}

	n2, _ := stack.Select(job.TaskGroups[0])
	m2 := ctx.Metrics()
	if n2 == nil {
		t.Fatalf("missing node %#v", m2)
	}

	// If we don't reset, this would be 2
	if m2.NodesEvaluated != 1 {
		t.Fatalf("should only be 2")
	}
}

func TestSystemStack_Select_DriverFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
	}
	zero := nodes[0]
	zero.Attributes["driver.foo"] = "1"

	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.TaskGroups[0].Tasks[0].Driver = "foo"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	zero.Attributes["driver.foo"] = "0"
	stack = NewSystemStack(ctx)
	stack.SetNodes(nodes)
	stack.SetJob(job)
	node, _ = stack.Select(job.TaskGroups[0])
	if node != nil {
		t.Fatalf("node not filtered %#v", node)
	}
}

func TestSystemStack_Select_ConstraintFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[1]
	zero.Attributes["kernel.name"] = "freebsd"

	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.Constraints[0].RTarget = "freebsd"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesFiltered != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassFiltered["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ConstraintFiltered["$attr.kernel.name = freebsd"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
}

func TestSystemStack_Select_BinPack_Overflow(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	zero.Reserved = zero.Resources
	one := nodes[1]

	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != one {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesExhausted != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassExhausted["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if len(met.Scores) != 1 {
		t.Fatalf("bad: %#v", met)
	}
}
<commit_msg>Skip unreliable time measurement assertions on Windows.<commit_after>package scheduler

import (
	"reflect"
	"runtime"
	"testing"

	"github.com/hashicorp/nomad/nomad/mock"
	"github.com/hashicorp/nomad/nomad/structs"
)

func TestServiceStack_SetNodes(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewGenericStack(false, ctx)

	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack.SetNodes(nodes)

	// Check that our scan limit is updated
	if stack.limit.limit != 3 {
		t.Fatalf("bad limit %d", stack.limit.limit)
	}

	out := collectFeasible(stack.source)
	if !reflect.DeepEqual(out, nodes) {
		t.Fatalf("bad: %#v", out)
	}
}

func TestServiceStack_SetJob(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewGenericStack(false, ctx)

	job := mock.Job()
	stack.SetJob(job)

	if stack.binPack.priority != job.Priority {
		t.Fatalf("bad")
	}
	if !reflect.DeepEqual(stack.jobConstraint.constraints, job.Constraints) {
		t.Fatalf("bad")
	}
}

func TestServiceStack_Select_Size(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
	}
	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	node, size := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}
	if size == nil {
		t.Fatalf("missing size")
	}

	if size.CPU != 500 || size.MemoryMB != 256 {
		t.Fatalf("bad: %#v", size)
	}

	// Note: On Windows time.Now currently has a best case granularity of 1ms.
	// We skip the following assertion on Windows because this test usually
	// runs too fast to measure an allocation time on Windows.
	met := ctx.Metrics()
	if runtime.GOOS != "windows" && met.AllocationTime == 0 {
		t.Fatalf("missing time")
	}
}

func TestServiceStack_Select_MetricsReset(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	n1, _ := stack.Select(job.TaskGroups[0])
	m1 := ctx.Metrics()
	if n1 == nil {
		t.Fatalf("missing node %#v", m1)
	}

	if m1.NodesEvaluated != 2 {
		t.Fatalf("should only be 2")
	}

	n2, _ := stack.Select(job.TaskGroups[0])
	m2 := ctx.Metrics()
	if n2 == nil {
		t.Fatalf("missing node %#v", m2)
	}

	// If we don't reset, this would be 4
	if m2.NodesEvaluated != 2 {
		t.Fatalf("should only be 2")
	}
}

func TestServiceStack_Select_DriverFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	zero.Attributes["driver.foo"] = "1"

	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.TaskGroups[0].Tasks[0].Driver = "foo"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}
}

func TestServiceStack_Select_ConstraintFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	zero.Attributes["kernel.name"] = "freebsd"

	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.Constraints[0].RTarget = "freebsd"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesFiltered != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassFiltered["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ConstraintFiltered["$attr.kernel.name = freebsd"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
}

func TestServiceStack_Select_BinPack_Overflow(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	one := nodes[1]
	one.Reserved = one.Resources

	stack := NewGenericStack(false, ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesExhausted != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassExhausted["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if len(met.Scores) != 1 {
		t.Fatalf("bad: %#v", met)
	}
}

func TestSystemStack_SetNodes(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewSystemStack(ctx)

	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack.SetNodes(nodes)

	out := collectFeasible(stack.source)
	if !reflect.DeepEqual(out, nodes) {
		t.Fatalf("bad: %#v", out)
	}
}

func TestSystemStack_SetJob(t *testing.T) {
	_, ctx := testContext(t)
	stack := NewSystemStack(ctx)

	job := mock.Job()
	stack.SetJob(job)

	if stack.binPack.priority != job.Priority {
		t.Fatalf("bad")
	}
	if !reflect.DeepEqual(stack.jobConstraint.constraints, job.Constraints) {
		t.Fatalf("bad")
	}
}

func TestSystemStack_Select_Size(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{mock.Node()}
	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	node, size := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}
	if size == nil {
		t.Fatalf("missing size")
	}

	if size.CPU != 500 || size.MemoryMB != 256 {
		t.Fatalf("bad: %#v", size)
	}

	// Note: On Windows time.Now currently has a best case granularity of 1ms.
	// We skip the following assertion on Windows because this test usually
	// runs too fast to measure an allocation time on Windows.
	met := ctx.Metrics()
	if runtime.GOOS != "windows" && met.AllocationTime == 0 {
		t.Fatalf("missing time")
	}
}

func TestSystemStack_Select_MetricsReset(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
		mock.Node(),
		mock.Node(),
	}
	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)
	n1, _ := stack.Select(job.TaskGroups[0])
	m1 := ctx.Metrics()
	if n1 == nil {
		t.Fatalf("missing node %#v", m1)
	}

	if m1.NodesEvaluated != 1 {
		t.Fatalf("should only be 1")
	}

	n2, _ := stack.Select(job.TaskGroups[0])
	m2 := ctx.Metrics()
	if n2 == nil {
		t.Fatalf("missing node %#v", m2)
	}

	// If we don't reset, this would be 2
	if m2.NodesEvaluated != 1 {
		t.Fatalf("should only be 2")
	}
}

func TestSystemStack_Select_DriverFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
	}
	zero := nodes[0]
	zero.Attributes["driver.foo"] = "1"

	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.TaskGroups[0].Tasks[0].Driver = "foo"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	zero.Attributes["driver.foo"] = "0"
	stack = NewSystemStack(ctx)
	stack.SetNodes(nodes)
	stack.SetJob(job)
	node, _ = stack.Select(job.TaskGroups[0])
	if node != nil {
		t.Fatalf("node not filtered %#v", node)
	}
}

func TestSystemStack_Select_ConstraintFilter(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[1]
	zero.Attributes["kernel.name"] = "freebsd"

	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	job.Constraints[0].RTarget = "freebsd"
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != zero {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesFiltered != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassFiltered["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ConstraintFiltered["$attr.kernel.name = freebsd"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
}

func TestSystemStack_Select_BinPack_Overflow(t *testing.T) {
	_, ctx := testContext(t)
	nodes := []*structs.Node{
		mock.Node(),
		mock.Node(),
	}
	zero := nodes[0]
	zero.Reserved = zero.Resources
	one := nodes[1]

	stack := NewSystemStack(ctx)
	stack.SetNodes(nodes)

	job := mock.Job()
	stack.SetJob(job)

	node, _ := stack.Select(job.TaskGroups[0])
	if node == nil {
		t.Fatalf("missing node %#v", ctx.Metrics())
	}

	if node.Node != one {
		t.Fatalf("bad")
	}

	met := ctx.Metrics()
	if met.NodesExhausted != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if met.ClassExhausted["linux-medium-pci"] != 1 {
		t.Fatalf("bad: %#v", met)
	}
	if len(met.Scores) != 1 {
		t.Fatalf("bad: %#v", met)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	aoc "github.com/rafax/aoc2016"
)

type key struct {
	string
}

var keyboard1 = [][]*key{[]*key{nil, nil, nil, nil, nil}, []*key{nil, &key{"1"}, &key{"2"}, &key{"3"}, nil},
	[]*key{nil, &key{"4"}, &key{"5"}, &key{"6"}, nil}, []*key{nil, &key{"7"}, &key{"8"}, &key{"9"}, nil}, []*key{nil, nil, nil, nil, nil}}

type Position struct {
	x, y     int
	keyboard [][]*key
}

func (p Position) Move(d aoc.Direction) Position {
	switch d {
	case aoc.U:
		if p.keyboard[p.x][p.y-1] != nil {
			return Position{p.x, p.y - 1, p.keyboard}
		}
	case aoc.R:
		if p.keyboard[p.x+1][p.y] != nil {
			return Position{p.x + 1, p.y, p.keyboard}
		}
	case aoc.D:
		if p.keyboard[p.x][p.y+1] != nil {
			return Position{p.x, p.y + 1, p.keyboard}
		}
	case aoc.L:
		if p.keyboard[p.x-1][p.y] != nil {
			return Position{p.x - 1, p.y, p.keyboard}
		}
	default:
		panic("Unrecognized direction" + string(d))
	}
	return p
}

func (p Position) Key() string {
	return p.keyboard[p.y][p.x].string
}

func main() {
	file, err := os.Open("in.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	pos := Position{x: 2, y: 2, keyboard: keyboard1}
	for scanner.Scan() {
		dir := scanner.Text()
		for _, in := range dir {
			d := aoc.ParseDirection(in)
			pos = pos.Move(d)
		}
		fmt.Print(pos.Key())
	}
}
<commit_msg>Get ready for part 2<commit_after>package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	aoc "github.com/rafax/aoc2016"
)

type key struct {
	string
}

var keyboard1 = [][]*key{[]*key{nil, nil, nil, nil, nil}, []*key{nil, &key{"1"}, &key{"2"}, &key{"3"}, nil},
	[]*key{nil, &key{"4"}, &key{"5"}, &key{"6"}, nil}, []*key{nil, &key{"7"}, &key{"8"}, &key{"9"}, nil}, []*key{nil, nil, nil, nil, nil}}

var keyboard2 = [][]*key{[]*key{nil, nil, nil, nil, nil, nil, nil},
	[]*key{nil, nil, nil, &key{"1"}, nil, nil, nil},
	[]*key{nil, nil, &key{"2"}, &key{"3"}, &key{"4"}, nil, nil},
	[]*key{nil, &key{"5"}, &key{"6"}, &key{"7"}, &key{"8"}, &key{"9"}, nil},
	[]*key{nil, nil, &key{"A"}, &key{"B"}, &key{"C"}, nil, nil},
	[]*key{nil, nil, nil, &key{"D"}, nil, nil, nil},
	[]*key{nil, nil, nil, nil, nil, nil, nil}}

type Position struct {
	x, y     int
	keyboard [][]*key
}

func (p Position) Move(d aoc.Direction) Position {
	switch d {
	case aoc.U:
		if p.keyboard[p.x][p.y-1] != nil {
			return Position{p.x, p.y - 1, p.keyboard}
		}
	case aoc.R:
		if p.keyboard[p.x+1][p.y] != nil {
			return Position{p.x + 1, p.y, p.keyboard}
		}
	case aoc.D:
		if p.keyboard[p.x][p.y+1] != nil {
			return Position{p.x, p.y + 1, p.keyboard}
		}
	case aoc.L:
		if p.keyboard[p.x-1][p.y] != nil {
			return Position{p.x - 1, p.y, p.keyboard}
		}
	default:
		panic("Unrecognized direction" + string(d))
	}
	return p
}

func (p Position) Key() string {
	return p.keyboard[p.y][p.x].string
}

func main() {
	file, err := os.Open("in.txt")
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	pos := Position{x: 2, y: 2, keyboard: keyboard1}
	for scanner.Scan() {
		dir := scanner.Text()
		for _, in := range dir {
			d := aoc.ParseDirection(in)
			pos = pos.Move(d)
		}
		fmt.Print(pos.Key())
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/lucasb-eyer/go-colorful"
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/clock"
	"github.com/ninjasphere/sphere-go-led-controller/ui"
	"github.com/ninjasphere/sphere-go-led-controller/util"
	"github.com/ninjasphere/usvc-lib/config"
)

var enableACPane = config.Bool(true, "ac.enable")
var tempAdjustInterval = config.Duration(time.Millisecond*300, "ac.airwheel.interval")
var airWheelReset = config.Duration(time.Millisecond*500, "ac.airwheel.reset")
var tapTimeout = config.Duration(time.Millisecond*500, "ac.tap.timeout")

var (
	red, _   = colorful.Hex("#fd9720")
	blue, _  = colorful.Hex("#45aff9")
	green, _ = colorful.Hex("#45f96b")
)

type ACPane struct {
	mode        string
	images      map[string]util.Image
	targetTemp  int
	currentTemp int
	controlled  bool

	thermostat *ninja.ServiceClient
	acstat     *ninja.ServiceClient

	lastTempAdjust   time.Time
	lastAirWheelTime time.Time
	lastAirWheel     *uint8
	countSinceLast   int
	ignoringTap      bool
	ignoreTapTimer   *time.Timer
}

func NewACPane(conn *ninja.Connection) *ACPane {

	pane := &ACPane{
		mode:        "off",
		controlled:  false,
		targetTemp:  666,
		currentTemp: 666,
		images: map[string]util.Image{
			"off":  util.LoadImage(util.ResolveImagePath("fan.gif")),
			"fan":  util.LoadImage(util.ResolveImagePath("fan-on.gif")),
			"cool": util.LoadImage(util.ResolveImagePath("fan-on-blue.gif")),
			"heat": util.LoadImage(util.ResolveImagePath("fan-on-red.gif")),
		},
	}

	pane.ignoreTapTimer = time.AfterFunc(0, func() {
		pane.ignoringTap = false
	})

	listening := make(map[string]bool)

	onState := func(protocol, event string, cb func(params *json.RawMessage)) {
		ui.GetChannelServicesContinuous("aircon", protocol, func(thing *model.Thing) bool {
			return true
		}, func(devices []*ninja.ServiceClient, err error) {
			if err != nil {
				log.Infof("Failed to update %s device: %s", protocol, err)
			} else {
				log.Infof("Got %d %s devices", len(devices), protocol)

				for _, device := range devices {

					log.Debugf("Checking %s device %s", protocol, device.Topic)

					if _, ok := listening[device.Topic]; !ok {

						// New device
						log.Infof("Got new %s device: %s", protocol, device.Topic)

						if protocol == "thermostat" {
							pane.thermostat = device
						}

						if protocol == "acstat" {
							pane.acstat = device
						}

						if protocol == "demandstat" {
							go func() {
								for {
									time.Sleep(time.Second)

									var state StateChangeNotification
									err := device.Call("get", nil, &state, time.Second*10)
									if err != nil {
										log.Errorf("Failed to fetch state from demandstat channel: %s", err)

										if strings.Contains(err.Error(), "timed out") {
											continue
										} else {
											break
										}

									}

									spew.Dump("Got demandstat state", state)

									pane.controlled = state.State == "STATE_ACTIVE"
								}
							}()
						}

						listening[device.Topic] = true

						device.OnEvent(event, func(params *json.RawMessage, values map[string]string) bool {
							cb(params)

							return true
						})
					}
				}
			}
		})
	}

	onState("temperature", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal temp from %s error:%s", *params, err)
		}

		pane.currentTemp = int(temp)

		log.Infof("Got the temp %d", pane.currentTemp)
	})

	onState("thermostat", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal thermostat from %s error:%s", *params, err)
		}

		pane.targetTemp = int(temp)

		log.Infof("Got the thermostat %d", pane.targetTemp)
	})

	onState("acstat", "state", func(params *json.RawMessage) {
		var state channels.ACState
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal acstat from %s error:%s", *params, err)
		}

		pane.mode = *state.Mode

		log.Infof("Got the ac mode %d", pane.mode)
	})

	onState("demandstat", "statechange", func(params *json.RawMessage) {

		spew.Dump("demandstat", params)

		var state StateChangeNotification
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal demandstat state from %s error:%s", *params, err)
		}

		pane.controlled = state.State == "STATE_ACTIVE"

		log.Infof("Got the demandstat state %d", pane.mode)
	})

	go ui.StartSearchTasks(conn)

	return pane
}

type StateChangeNotification struct {
	State string `json:"state"` // the current state of the controller
}

func (p *ACPane) KeepAwake() bool {
	return true
}

func (p *ACPane) Locked() bool {
	return true
}

func (p *ACPane) IsEnabled() bool {
	return enableACPane
}

func (p *ACPane) Gesture(gesture *gestic.GestureMessage) {

	if p.acstat != nil && !p.ignoringTap && gesture.Tap.Active() {
		log.Infof("AC tap!")

		p.ignoringTap = true
		p.ignoreTapTimer.Reset(tapTimeout)

		switch p.mode {
		case "off":
			p.mode = "fan"
		case "fan":
			p.mode = "cool"
		case "cool":
			p.mode = "heat"
		case "heat":
			p.mode = "off"
		}

		p.acstat.Call("set", channels.ACState{
			Mode: &p.mode,
		}, nil, 0)

	}

	if p.thermostat != nil && p.targetTemp != 666 && p.lastAirWheel == nil || gesture.AirWheel.Counter != int(*p.lastAirWheel) {

		if time.Since(p.lastAirWheelTime) > airWheelReset {
			p.lastAirWheel = nil
		}

		if p.countSinceLast > gesture.AirWheel.CountSinceLast {
			p.lastAirWheel = nil
		}

		p.countSinceLast = gesture.AirWheel.CountSinceLast

		p.lastAirWheelTime = time.Now()

		log.Debugf("Airwheel: %d", gesture.AirWheel.Counter)

		if p.lastAirWheel != nil {
			offset := int(gesture.AirWheel.Counter) - int(*p.lastAirWheel)

			if offset > 30 {
				offset -= 255
			}

			if offset < -30 {
				offset += 255
			}

			log.Debugf("Airwheel New: %d Offset: %d Last: %d", gesture.AirWheel.Counter, offset, *p.lastAirWheel)

			log.Debugf("Current temp %f", p.targetTemp)

			log.Debugf("Temp offset %f", float64(offset)/255.0)

			if time.Since(p.lastTempAdjust) < tempAdjustInterval {
				log.Debugf("Temp rate limited")
			} else {
				p.lastTempAdjust = time.Now()
				log.Debugf("Temp NOT rate limited")
				if offset > 0 {
					p.targetTemp += 1
				} else {
					p.targetTemp -= 1
				}

				p.thermostat.Call("set", p.targetTemp, nil, 0)

			}

		}

		val := uint8(gesture.AirWheel.Counter)
		p.lastAirWheel = &val
	}
}

func (p *ACPane) Render() (*image.RGBA, error) {

	img := image.NewRGBA(image.Rect(0, 0, 16, 16))

	drawText := func(text string, col color.Color, x, y int) {
		clock.Font.DrawString(img, x, y, text, col)
	}

	draw.Draw(img, img.Bounds(), p.images[p.mode].GetNextFrame(), img.Bounds().Min, draw.Src)

	targetCol := green
	if p.controlled {
		//drawText("$", green, 0, 11)
		h, s, v := targetCol.Hsv()
		targetCol = colorful.Hsv(sinTime(h), s, v)
	}

	if p.currentTemp != 666 {

		if p.currentTemp > p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), red, 0, 11)
		} else if p.currentTemp < p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), blue, 0, 11)
		}

	}

	if p.targetTemp != 666 {
		drawText(fmt.Sprintf("%02d", p.targetTemp), targetCol, 9, 11)
	}

	return img, nil
}

func (p *ACPane) IsDirty() bool {
	return true
}

func sinTime(x float64) float64 {
	t := float64(time.Now().Nanosecond()/int(time.Millisecond)) / 1000.0
	t = math.Sin((t - 0.5) * (2 * math.Pi))
	t = (t + 1) / 2
	return x * t
}
<commit_msg>Change name of notification from demandstat/statechange to demand/controlstat.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"math"
	"strings"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/lucasb-eyer/go-colorful"
	"github.com/ninjasphere/gestic-tools/go-gestic-sdk"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/channels"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/sphere-go-led-controller/fonts/clock"
	"github.com/ninjasphere/sphere-go-led-controller/ui"
	"github.com/ninjasphere/sphere-go-led-controller/util"
	"github.com/ninjasphere/usvc-lib/config"
)

var enableACPane = config.Bool(true, "ac.enable")
var tempAdjustInterval = config.Duration(time.Millisecond*300, "ac.airwheel.interval")
var airWheelReset = config.Duration(time.Millisecond*500, "ac.airwheel.reset")
var tapTimeout = config.Duration(time.Millisecond*500, "ac.tap.timeout")

var (
	red, _   = colorful.Hex("#fd9720")
	blue, _  = colorful.Hex("#45aff9")
	green, _ = colorful.Hex("#45f96b")
)

type ACPane struct {
	mode        string
	images      map[string]util.Image
	targetTemp  int
	currentTemp int
	controlled  bool

	thermostat *ninja.ServiceClient
	acstat     *ninja.ServiceClient

	lastTempAdjust   time.Time
	lastAirWheelTime time.Time
	lastAirWheel     *uint8
	countSinceLast   int
	ignoringTap      bool
	ignoreTapTimer   *time.Timer
}

func NewACPane(conn *ninja.Connection) *ACPane {

	pane := &ACPane{
		mode:        "off",
		controlled:  false,
		targetTemp:  666,
		currentTemp: 666,
		images: map[string]util.Image{
			"off":  util.LoadImage(util.ResolveImagePath("fan.gif")),
			"fan":  util.LoadImage(util.ResolveImagePath("fan-on.gif")),
			"cool": util.LoadImage(util.ResolveImagePath("fan-on-blue.gif")),
			"heat": util.LoadImage(util.ResolveImagePath("fan-on-red.gif")),
		},
	}

	pane.ignoreTapTimer = time.AfterFunc(0, func() {
		pane.ignoringTap = false
	})

	listening := make(map[string]bool)

	onState := func(protocol, event string, cb func(params *json.RawMessage)) {
		ui.GetChannelServicesContinuous("aircon", protocol, func(thing *model.Thing) bool {
			return true
		}, func(devices []*ninja.ServiceClient, err error) {
			if err != nil {
				log.Infof("Failed to update %s device: %s", protocol, err)
			} else {
				log.Infof("Got %d %s devices", len(devices), protocol)

				for _, device := range devices {

					log.Debugf("Checking %s device %s", protocol, device.Topic)

					if _, ok := listening[device.Topic]; !ok {

						// New device
						log.Infof("Got new %s device: %s", protocol, device.Topic)

						if protocol == "thermostat" {
							pane.thermostat = device
						}

						if protocol == "acstat" {
							pane.acstat = device
						}

						if protocol == "demand" {
							go func() {
								for {
									time.Sleep(time.Second)

									var state StateChangeNotification
									err := device.Call("getControlState", nil, &state, time.Second*10)
									if err != nil {
										log.Errorf("Failed to fetch state from demand channel: %s", err)

										if strings.Contains(err.Error(), "timed out") {
											continue
										} else {
											break
										}

									}

									spew.Dump("Got demand state", state)

									pane.controlled = state.State == "STATE_ACTIVE"
								}
							}()
						}

						listening[device.Topic] = true

						device.OnEvent(event, func(params *json.RawMessage, values map[string]string) bool {
							cb(params)

							return true
						})
					}
				}
			}
		})
	}

	onState("temperature", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal temp from %s error:%s", *params, err)
		}

		pane.currentTemp = int(temp)

		log.Infof("Got the temp %d", pane.currentTemp)
	})

	onState("thermostat", "state", func(params *json.RawMessage) {
		var temp float64
		err := json.Unmarshal(*params, &temp)
		if err != nil {
			log.Infof("Failed to unmarshal thermostat from %s error:%s", *params, err)
		}

		pane.targetTemp = int(temp)

		log.Infof("Got the thermostat %d", pane.targetTemp)
	})

	onState("acstat", "state", func(params *json.RawMessage) {
		var state channels.ACState
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal acstat from %s error:%s", *params, err)
		}

		pane.mode = *state.Mode

		log.Infof("Got the ac mode %d", pane.mode)
	})

	onState("demand", "controlstate", func(params *json.RawMessage) {

		spew.Dump("demand/controlstate", params)

		var state StateChangeNotification
		err := json.Unmarshal(*params, &state)
		if err != nil {
			log.Infof("Failed to unmarshal demandstat state from %s error:%s", *params, err)
		}

		pane.controlled = state.State == "STATE_ACTIVE"

		log.Infof("Got the demandstat state %d", pane.mode)
	})

	go ui.StartSearchTasks(conn)

	return pane
}

type StateChangeNotification struct {
	State string `json:"state"` // the current state of the controller
}

func (p *ACPane) KeepAwake() bool {
	return true
}

func (p *ACPane) Locked() bool {
	return true
}

func (p *ACPane) IsEnabled() bool {
	return enableACPane
}

func (p *ACPane) Gesture(gesture *gestic.GestureMessage) {

	if p.acstat != nil && !p.ignoringTap && gesture.Tap.Active() {
		log.Infof("AC tap!")

		p.ignoringTap = true
		p.ignoreTapTimer.Reset(tapTimeout)

		switch p.mode {
		case "off":
			p.mode = "fan"
		case "fan":
			p.mode = "cool"
		case "cool":
			p.mode = "heat"
		case "heat":
			p.mode = "off"
		}

		p.acstat.Call("set", channels.ACState{
			Mode: &p.mode,
		}, nil, 0)

	}

	if p.thermostat != nil && p.targetTemp != 666 && p.lastAirWheel == nil || gesture.AirWheel.Counter != int(*p.lastAirWheel) {

		if time.Since(p.lastAirWheelTime) > airWheelReset {
			p.lastAirWheel = nil
		}

		if p.countSinceLast > gesture.AirWheel.CountSinceLast {
			p.lastAirWheel = nil
		}

		p.countSinceLast = gesture.AirWheel.CountSinceLast

		p.lastAirWheelTime = time.Now()

		log.Debugf("Airwheel: %d", gesture.AirWheel.Counter)

		if p.lastAirWheel != nil {
			offset := int(gesture.AirWheel.Counter) - int(*p.lastAirWheel)

			if offset > 30 {
				offset -= 255
			}

			if offset < -30 {
				offset += 255
			}

			log.Debugf("Airwheel New: %d Offset: %d Last: %d", gesture.AirWheel.Counter, offset, *p.lastAirWheel)

			log.Debugf("Current temp %f", p.targetTemp)

			log.Debugf("Temp offset %f", float64(offset)/255.0)

			if time.Since(p.lastTempAdjust) < tempAdjustInterval {
				log.Debugf("Temp rate limited")
			} else {
				p.lastTempAdjust = time.Now()
				log.Debugf("Temp NOT rate limited")
				if offset > 0 {
					p.targetTemp += 1
				} else {
					p.targetTemp -= 1
				}

				p.thermostat.Call("set", p.targetTemp, nil, 0)

			}

		}

		val := uint8(gesture.AirWheel.Counter)
		p.lastAirWheel = &val
	}
}

func (p *ACPane) Render() (*image.RGBA, error) {

	img := image.NewRGBA(image.Rect(0, 0, 16, 16))

	drawText := func(text string, col color.Color, x, y int) {
		clock.Font.DrawString(img, x, y, text, col)
	}

	draw.Draw(img, img.Bounds(), p.images[p.mode].GetNextFrame(), img.Bounds().Min, draw.Src)

	targetCol := green
	if p.controlled {
		//drawText("$", green, 0, 11)
		h, s, v := targetCol.Hsv()
		targetCol = colorful.Hsv(sinTime(h), s, v)
	}

	if p.currentTemp != 666 {

		if p.currentTemp > p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), red, 0, 11)
		} else if p.currentTemp < p.targetTemp {
			drawText(fmt.Sprintf("%02d", p.currentTemp), blue, 0, 11)
		}

	}

	if p.targetTemp != 666 {
		drawText(fmt.Sprintf("%02d", p.targetTemp), targetCol, 9, 11)
	}

	return img, nil
}

func (p *ACPane) IsDirty() bool {
	return true
}

func sinTime(x float64) float64 {
	t := float64(time.Now().Nanosecond()/int(time.Millisecond)) / 1000.0
	t = math.Sin((t - 0.5) * (2 * math.Pi))
	t = (t + 1) / 2
	return x * t
}
<|endoftext|>


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

package lxdclient

import (
	"github.com/juju/errors"
)

type rawProfileClient interface {
	ProfileCreate(name string) error
	ListProfiles() ([]string, error)
	SetProfileConfigItem(name, key, value string) error
}

type profileClient struct {
	raw rawProfileClient
}

// CreateProfile attempts to create a new lxc profile and set the given config.
func (p profileClient) CreateProfile(name string, config map[string]string) error {
	if err := p.raw.ProfileCreate(name); err != nil {
		//TODO(wwitzel3) use HasProfile to generate a more useful AlreadyExists error
		return errors.Trace(err)
	}

	for k, v := range config {
		if err := p.raw.SetProfileConfigItem(name, k, v); err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// HasProfile returns true/false if the profile exists.
func (p profileClient) HasProfile(name string) (bool, error) {
	profiles, err := p.raw.ListProfiles()
	if err != nil {
		return false, errors.Trace(err)
	}
	for _, profile := range profiles {
		if profile == name {
			return true, nil
		}
	}
	return false, nil
}
<commit_msg>Add a missing build constraint.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

// +build go1.3

package lxdclient

import (
	"github.com/juju/errors"
)

type rawProfileClient interface {
	ProfileCreate(name string) error
	ListProfiles() ([]string, error)
	SetProfileConfigItem(name, key, value string) error
}

type profileClient struct {
	raw rawProfileClient
}

// CreateProfile attempts to create a new lxc profile and set the given config.
func (p profileClient) CreateProfile(name string, config map[string]string) error {
	if err := p.raw.ProfileCreate(name); err != nil {
		//TODO(wwitzel3) use HasProfile to generate a more useful AlreadyExists error
		return errors.Trace(err)
	}

	for k, v := range config {
		if err := p.raw.SetProfileConfigItem(name, k, v); err != nil {
			return errors.Trace(err)
		}
	}
	return nil
}

// HasProfile returns true/false if the profile exists.
func (p profileClient) HasProfile(name string) (bool, error) {
	profiles, err := p.raw.ListProfiles()
	if err != nil {
		return false, errors.Trace(err)
	}
	for _, profile := range profiles {
		if profile == name {
			return true, nil
		}
	}
	return false, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
)

var (
	flBroker        string
	flConsumerGroup string
	flTopic         string
	flPartition     int
	flNewest        bool

	fsConsumerGroup = flag.NewFlagSet("cgo", flag.ContinueOnError)
	fsGetOffset     = flag.NewFlagSet("go", flag.ContinueOnError)
)

func init() {
	flag.Usage = printUsage

	fsConsumerGroup.StringVar(&flBroker, "b", "", "The broker to use")
	fsConsumerGroup.StringVar(&flConsumerGroup, "c", "", "The consumer group")
	fsConsumerGroup.StringVar(&flTopic, "t", "", "The topic")
	fsConsumerGroup.IntVar(&flPartition, "p", -1, "The partition")

	fsGetOffset.StringVar(&flBroker, "b", "", "The broker to use")
	fsGetOffset.StringVar(&flTopic, "t", "", "The topic")
	fsGetOffset.IntVar(&flPartition, "p", -1, "The partition")
	fsGetOffset.BoolVar(&flNewest, "n", true, "Get the newest offset instead of the oldest")
}

func printUsage() {
	fmt.Fprintf(os.Stderr, "Usage of %s\n", os.Args[0])
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\nSubcommands:\n\ncgo (consumer group get offset)\n")
	fsConsumerGroup.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\ngo (get offset)\n")
	fsGetOffset.PrintDefaults()
}
<commit_msg>Make the broker flag global<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
)

var (
	flBroker        string
	flConsumerGroup string
	flTopic         string
	flPartition     int
	flNewest        bool

	fsConsumerGroup = flag.NewFlagSet("cgo", flag.ContinueOnError)
	fsGetOffset     = flag.NewFlagSet("go", flag.ContinueOnError)
)

func init() {
	flag.Usage = printUsage

	flag.StringVar(&flBroker, "b", "", "The broker to use")

	fsConsumerGroup.StringVar(&flConsumerGroup, "c", "", "The consumer group")
	fsConsumerGroup.StringVar(&flTopic, "t", "", "The topic")
	fsConsumerGroup.IntVar(&flPartition, "p", -1, "The partition")

	fsGetOffset.StringVar(&flTopic, "t", "", "The topic")
	fsGetOffset.IntVar(&flPartition, "p", -1, "The partition")
	fsGetOffset.BoolVar(&flNewest, "n", true, "Get the newest offset instead of the oldest")
}

func printUsage() {
	fmt.Fprintf(os.Stderr, "Usage of %s\n", os.Args[0])
	flag.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\nSubcommands:\n\ncgo (consumer group get offset)\n")
	fsConsumerGroup.PrintDefaults()
	fmt.Fprintf(os.Stderr, "\ngo (get offset)\n")
	fsGetOffset.PrintDefaults()
}
<|endoftext|>


<commit_before>package seabird

import (
	"github.com/thoj/go-ircevent"

	"fmt"
	"strings"
	"unicode"
)

// Connect handler
func (b *Bot) connect(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	// Reset our user map
	b.Users = make(map[string]*User)

	// Run all our raw
	for _, v := range b.Config.Cmds {
		b.Conn.SendRaw(v)
	}
}

// Msg handler
func (b *Bot) msg(e *irc.Event) {
	nick := b.Conn.GetNick()

	// TODO: Handle actual mentions?

	// This callback only handles stuff we've added callbacks for
	if strings.HasPrefix(e.Message, b.Config.Prefix) {
		msgParts := strings.SplitN(e.Message, " ", 2)
		switch len(msgParts) {
		case 2:
			e.Message = msgParts[1]
		case 1:
			e.Message = ""
		}
		cmd := msgParts[0][len(b.Config.Prefix):]
		if cb, ok := b.Commands[cmd]; ok {
			cb(e)
		}
	} else if strings.HasPrefix(e.Message, nick) {
		// TODO: We may want to ensure at least one space between nick and msg
		// NOTE: We need a copy to not mess up other PRIVMSG handlers
		m := *e
		m.Message = e.Message[len(nick):]
		m.Message = strings.TrimLeftFunc(m.Message, func(r rune) bool {
			// http://weknowgifs.com/wp-content/uploads/2013/03/its-magic-shia-labeouf-gif.gif
			return unicode.IsPunct(r) || unicode.IsSpace(r)
		})
		for _, v := range b.MentionCommands {
			v(&m)
		}
	}
}

func (b *Bot) join(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	if e.Nick != b.Conn.GetNick() {
		b.addChannelToNick(e.Message, e.Nick)
	} else {
		// For safety, if we join the channel,
		// make sure we aren't tracking anyone there yet
		for _, user := range b.Users {
			b.removeChannelFromUser(e.Message, user)
		}
	}
}

func (b *Bot) whoReply(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	fmt.Println(len(e.Arguments))
	if len(e.Arguments) < 7 {
		// TODO: Error here
		return
	}

	fmt.Printf("%+v\n", e.Arguments)
	b.addChannelToNick(e.Arguments[1], e.Arguments[5])
}

func (b *Bot) nick(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	data := b.GetUser(e.Nick)
	if len(data.Channels) == 0 {
		return
	}

	data.CurrentNick = e.Message

	delete(b.Users, e.Nick)
	b.Users[e.Message] = data
}

func (b *Bot) addChannelToNick(channel string, nick string) {
	// NOTE: This assumes the UserLock is already acquired
	user := b.GetUser(nick)

	for i := 0; i < len(user.Channels); i++ {
		if user.Channels[i] == channel {
			return
		}
	}

	user.Channels = append(user.Channels, channel)
	b.Users[nick] = user
}

func (b *Bot) removeChannelFromUser(channel string, user *User) {
	// NOTE: This assumes the UserLock is already acquired
	for i := 0; i < len(user.Channels); i++ {
		if user.Channels[i] == channel {
			// Swap with last element and shrink slice
			user.Channels[i] = user.Channels[len(user.Channels)-1]
			user.Channels = user.Channels[:len(user.Channels)-1]
			break
		}
	}

	if len(user.Channels) == 0 {
		// Removing user
		delete(b.Users, user.CurrentNick)
	}
}

func (b *Bot) part(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	if len(e.Arguments) < 1 {
		// TODO: Error here
		return
	}

	if e.Nick != b.Conn.GetNick() {
		if user, ok := b.Users[e.Nick]; ok {
			b.removeChannelFromUser(e.Arguments[1], user)
		}
	} else {
		// At this point, we left, so clean up that chanel
		for _, user := range b.Users {
			b.removeChannelFromUser(e.Arguments[1], user)
		}
	}
}

func (b *Bot) quit(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	// TODO: Implement this
}
<commit_msg>Doesn't die on part any more. Fixes #2.<commit_after>package seabird

import (
	"github.com/thoj/go-ircevent"

	"fmt"
	"strings"
	"unicode"
)

// Connect handler
func (b *Bot) connect(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	// Reset our user map
	b.Users = make(map[string]*User)

	// Run all our raw
	for _, v := range b.Config.Cmds {
		b.Conn.SendRaw(v)
	}
}

// Msg handler
func (b *Bot) msg(e *irc.Event) {
	nick := b.Conn.GetNick()

	// TODO: Handle actual mentions?

	// This callback only handles stuff we've added callbacks for
	if strings.HasPrefix(e.Message, b.Config.Prefix) {
		msgParts := strings.SplitN(e.Message, " ", 2)
		switch len(msgParts) {
		case 2:
			e.Message = msgParts[1]
		case 1:
			e.Message = ""
		}
		cmd := msgParts[0][len(b.Config.Prefix):]
		if cb, ok := b.Commands[cmd]; ok {
			cb(e)
		}
	} else if strings.HasPrefix(e.Message, nick) {
		// TODO: We may want to ensure at least one space between nick and msg
		// NOTE: We need a copy to not mess up other PRIVMSG handlers
		m := *e
		m.Message = e.Message[len(nick):]
		m.Message = strings.TrimLeftFunc(m.Message, func(r rune) bool {
			// http://weknowgifs.com/wp-content/uploads/2013/03/its-magic-shia-labeouf-gif.gif
			return unicode.IsPunct(r) || unicode.IsSpace(r)
		})
		for _, v := range b.MentionCommands {
			v(&m)
		}
	}
}

func (b *Bot) join(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	if e.Nick != b.Conn.GetNick() {
		b.addChannelToNick(e.Message, e.Nick)
	} else {
		// For safety, if we join the channel,
		// make sure we aren't tracking anyone there yet
		for _, user := range b.Users {
			b.removeChannelFromUser(e.Message, user)
		}
	}
}

func (b *Bot) whoReply(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	fmt.Println(len(e.Arguments))
	if len(e.Arguments) < 7 {
		// TODO: Error here
		return
	}

	fmt.Printf("%+v\n", e.Arguments)
	b.addChannelToNick(e.Arguments[1], e.Arguments[5])
}

func (b *Bot) nick(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	data := b.GetUser(e.Nick)
	if len(data.Channels) == 0 {
		return
	}

	data.CurrentNick = e.Message

	delete(b.Users, e.Nick)
	b.Users[e.Message] = data
}

func (b *Bot) addChannelToNick(channel string, nick string) {
	// NOTE: This assumes the UserLock is already acquired
	user := b.GetUser(nick)

	for i := 0; i < len(user.Channels); i++ {
		if user.Channels[i] == channel {
			return
		}
	}

	user.Channels = append(user.Channels, channel)
	b.Users[nick] = user
}

func (b *Bot) removeChannelFromUser(channel string, user *User) {
	// NOTE: This assumes the UserLock is already acquired
	for i := 0; i < len(user.Channels); i++ {
		if user.Channels[i] == channel {
			// Swap with last element and shrink slice
			user.Channels[i] = user.Channels[len(user.Channels)-1]
			user.Channels = user.Channels[:len(user.Channels)-1]
			break
		}
	}

	if len(user.Channels) == 0 {
		// Removing user
		delete(b.Users, user.CurrentNick)
	}
}

func (b *Bot) part(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	if len(e.Arguments) < 1 {
		// TODO: Error here
		return
	}

	if e.Nick != b.Conn.GetNick() {
		if user, ok := b.Users[e.Nick]; ok {
			b.removeChannelFromUser(e.Arguments[0], user)
		}
	} else {
		// At this point, we left, so clean up that chanel
		for _, user := range b.Users {
			b.removeChannelFromUser(e.Arguments[0], user)
		}
	}
}

func (b *Bot) quit(e *irc.Event) {
	b.UserLock.Lock()
	defer b.UserLock.Unlock()

	// TODO: Implement this
}
<|endoftext|>


<commit_before>// creates compressed archives.
//
// Go's `archive` package, supports `tar` and `zip`
// Go's `compress` package supports: bzip2, flate, gzip, lzw, zlib
//
// Carchivum supports zip and tar. For tar, archiver also supports
// the following compression:
//
// When using archiver, compression is not optional.
//
package carchivum

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	FmtUnsupported Format = iota
	FmtGzip
	FmtTar
	FmtTar1
	FmtTar2
	FmtZip
	FmtZipEmpty
	FmtZipSpanned
	FmtBzip2
	FmtLZH
	FmtLZW
	FmtRAR
	FmtRAROld
)

// Header information for common archive/compression formats.
// Zip includes: zip, jar, odt, ods, odp, docx, xlsx, pptx, apx, odf, ooxml
var (
	headerGzip []byte = []byte{0x1f, 0x8b}
	headerTar1 []byte = []byte{0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30} // offset: 257
	headerTar2 []byte = []byte{0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x20, 0x00} // offset: 257
	headerZip []byte = []byte{0x50, 0x4b, 0x03, 0x04}
	headerZipEmpty []byte = []byte{0x50, 0x4b, 0x05, 0x06}
	headerZipSpanned []byte = []byte{0x50, 0x4b, 0x07, 0x08}
	headerBzip2 []byte = []byte{0x42, 0x5a, 0x68}
	headerLZH []byte = []byte{0x1F, 0xa0}
	headerLZW []byte = []byte{0x1F, 0x9d}
	headerRAR []byte = []byte{0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00}
	headerRAROld []byte = []byte{0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}
)

func GetFileFormat(r io.ReaderAt) (Format, error) {
	h := make([]byte, 8, 8)

	r.ReadAt(h, 0)

	if bytes.Equal(headerGzip, h[0:2]) {
		return FmtGzip, nil
	}

	if bytes.Equal(headerZip, h[0:4]) {
		return FmtZip, nil
	}

	r.ReadAt(h, 257)
	if bytes.Equal(headerTar1, h) || bytes.Equal(headerTar2, h) {
		return FmtTar, nil
	}

	// unsupported
	if bytes.Equal(headerRAROld, h) {
		return FmtUnsupported, FmtRAROld.NotSupportedError()
	}

	if bytes.Equal(headerRAR, h) {
		return FmtUnsupported, FmtRAR.NotSupportedError()
	}

	r.ReadAt(h, 0)
	if bytes.Equal(headerZipEmpty, h[0:4]) {
		return FmtUnsupported, FmtZipEmpty.NotSupportedError()
	}

	if bytes.Equal(headerZipSpanned, h[0:4]) {
		return FmtUnsupported, FmtZipSpanned.NotSupportedError()
	}

	if bytes.Equal(headerBzip2, h[0:3]) {
		return FmtUnsupported, FmtBzip2.NotSupportedError()
	}

	if bytes.Equal(headerLZW, h[0:2]) {
		return FmtUnsupported, FmtLZW.NotSupportedError()
	}

	if bytes.Equal(headerLZH, h[0:2]) {
		return FmtUnsupported, FmtLZH.NotSupportedError()
	}

	return FmtUnsupported, FmtUnsupported.NotSupportedError()
}

type (
	Format int
)

func (f Format) ToString() string {
	switch f {
	case FmtGzip:
		return "gzip"
	case FmtTar1, FmtTar2:
		return "tar"
	case FmtZip:
		return "zip"
	case FmtZipEmpty:
		return "empty zip archive"
	case FmtZipSpanned:
		return "spanned zip archive"
	case FmtBzip2:
		return "bzip2"
	case FmtLZH:
		return "LZH"
	case FmtLZW:
		return "LZW"
	case FmtRAR:
		return "RAR post 5.0"
	case FmtRAROld:
		return  "RAR pre 1.5"
	}
	return "unsupported"
}

func (f Format) NotSupportedError() error {
	switch f {
	case FmtZipEmpty:
		return fmt.Errorf("empty zip archive is not supported")
	case FmtZipSpanned:
		return fmt.Errorf("spanned zip archive is not supported")
	case FmtBzip2 :
		return fmt.Errorf("bzip2 is not supported")
	case FmtLZH:
		return fmt.Errorf("LZH is not supported")
	case FmtLZW:
		return fmt.Errorf("LZW is not supported")
	case FmtRAR:
		return fmt.Errorf("RAR post 5.0 is not supported")
	case FmtRAROld:
		return fmt.Errorf("RAR pre 1.5 is not supported")
	}
	return fmt.Errorf("unsupported format error, more specific information unavailable")
}

var defaultFormat Format = FmtGzip

// Options
var AppendDate bool
var TimeFormat string = time.RFC3339
var UseNano bool
var Separator string = "-"
var MakeUnique bool = false
// default max random number for random number generation.
var MaxRand = 10000

// we assume this count isn't going to change during runtime
var cpu int = runtime.NumCPU()

// Arbitrarily set the multiplier to some default value.
var CPUMultiplier int = 4

// Car is a Compressed Archive. The struct holds information about Cars and
// their processing.
type Car struct {
	// Name of the archive, this includes path information, if any.
	Name string
	UseLongExt bool
	UseFullpath bool

	// Create operation modifiers
	Owner int
	Group int
	Mode os.FileMode

	// Extract operation modifiers

	// Local file selection
	// List of files to delete if applicable.
	Delete []string
	DeleteFiles bool

	// Exclude file processing
	Exclude string
	ExcludeExt []string
	ExcludeExtCount int
	ExcludeAnchored string

	// Include file processing
	Include string
	IncludeExt []string
	IncludeExtCount int
	IncludeAnchored string

	Newer string
	NewerMTime string
	NewerFile string

	// Processing queue
	FileCh	chan *os.File

	// Other Counters
	counterLock sync.Mutex
	files int32
	bytes int64
	compressedBytes int64

	// timer
	t0 time.Time
	𝛥t float64
}

func (c *Car) setDelta() {
	c.𝛥t = float64(time.Since(c.t0)) / 1e9
}

func (c *Car) Delta() float64 {
	return c.𝛥t
}

func (c *Car) Message() string {
	return fmt.Sprintf("%q created in %4f seconds\n%d files totalling %d bytes were processed", c.Name, c.𝛥t, c.files, c.bytes)
}

// addFile  reads a file and pipes it to the zipper goroutine.
func (c *Car) AddFile(root, p string, fi os.FileInfo, err error) error {
	logger.Debugf("root: %s, p: %s, fi.Name: %s", root, p, fi.Name())
	// Don't add symlinks, otherwise would have to code some cycle
	// detection amongst other stuff.
	if fi.Mode() & os.ModeSymlink == os.ModeSymlink {
		logger.Debugf("don't follow symlinks: %q", p)
		return nil
	}

	add, err := c.addFile(root, p)
	if err != nil {
		return err
	}
	if !add {
		return nil
	}

	var relPath string
	relPath, err = filepath.Rel(root, p)
	if err != nil {
		logger.Error(err)
		return err
	}

	if relPath == "," {
		logger.Debug("Don't add relative root")
		return nil
	}

	if !c.UseFullpath {
		p = filepath.Join(filepath.Base(root), relPath)
	}

	f, err := os.Open(p)
	if err != nil {
		logger.Error(err)
		return err
	}

	c.counterLock.Lock()
	c.files++
	c.bytes += fi.Size()
	c.counterLock.Unlock()

	c.FileCh <- f

	return nil
}

func (c *Car) addFile(root, p string) (bool, error) {
	if strings.HasSuffix(root, p) {
		logger.Debugf("%s | %s, don't add if source is the source directory", root, p)
		return false, nil
	}

	b, err := c.includeFile(root, p)
	if err != nil {
		return false, err
	}
	if !b {
		logger.Debugf("don't include %q", p)
		return false, nil
	}

	b, err = c.excludeFile(root, p)
	if err != nil {
		return false, err
	}
	if b {
		logger.Debugf("exclude %q", p)
		return false, nil
	}

	return true, nil
}

func (c *Car) includeFile(root, p string) (bool, error) {
	logger.Infof("%sroot: %s c.IncludeAnchored %s", root, p, c.IncludeAnchored)
	if c.IncludeAnchored != "" {
		logger.Info(filepath.Base(p))
		if strings.HasPrefix(filepath.Base(c.IncludeAnchored), p) {
			logger.Info("has prefix")
			return true, nil
		}
	}

	// since we are just evaluating a file, we use match and look at the
	// fullpath
	if c.Include != "" {
		matches, err := filepath.Match(c.Include, filepath.Join(root, p))
		if err != nil {
			return false, err
		}

		if matches {
			return true, nil
		}
	}

	if c.IncludeExtCount != 0 {
		for _, ext := range c.IncludeExt {
			if strings.HasSuffix(filepath.Base(p), "." + ext) {
				return true, nil
			}
		}
	}

	return false, nil
}

func (c *Car) excludeFile(root, p string) (bool, error) {
	logger.Infof("%s c.ExcludeAnchored %s", p, c.ExcludeAnchored)
	if c.ExcludeAnchored != "" {
		logger.Info(filepath.Base(p))
		if strings.HasPrefix(filepath.Base(p), c.ExcludeAnchored) {
			logger.Info("has prefix")
			return true, nil
		}
	}

	// since we are just evaluating a file, we use match and look at the
	// fullpath
	if c.Exclude != "" {
		matches, err := filepath.Match(c.Exclude, filepath.Join(root, p))
		if err != nil {
			return false, err
		}

		if matches {
			return true, nil
		}
	}

	if c.ExcludeExtCount != 0 {
		for _, ext := range c.ExcludeExt {
			if strings.HasSuffix(filepath.Base(p), "." + ext) {
				return true, nil
			}
		}
	}

	return false, nil
}

func ParseFormat(s string) (Format, error) {
	switch s {
	case "gzip", "tar.gz", "tgz":
		return FmtGzip, nil
	case "tar":
		return FmtTar, nil
	case "zip":
		return FmtZip, nil
	}

	return FmtUnsupported, FmtUnsupported.NotSupportedError()
}

func formattedNow() string {
	return time.Now().Local().Format(TimeFormat)
}

func getFileParts(s string) (dir, file, ext string, err error) {
	// see if there is path involved, if there is, get the last part of it
	dir, filename := filepath.Split(s)
	parts := strings.Split(filename, ".")
	l := len(parts)
	switch l {
	case 2:
		file := parts[0]
		ext := parts[1]
		return dir, file, ext, nil
	case 1:
		file := parts[0]
		return dir, file, ext, nil
	case 0:
		err := fmt.Errorf("no destination filename found in %s", s)
		logger.Error(err)
		return dir, file, ext, err
	default:
		// join all but the last parts together with a "."
		file := strings.Join(parts[0:l-1], ".")
		ext := parts[l-1]
		return dir, file, ext, nil
	}

	err = fmt.Errorf("unable to determine destination filename and extension")
	logger.Error(err)
	return dir, file, ext, err
}
<commit_msg>renamed file filter methods and created one for headerinfo processsing(mtime support)<commit_after>// creates compressed archives.
//
// Go's `archive` package, supports `tar` and `zip`
// Go's `compress` package supports: bzip2, flate, gzip, lzw, zlib
//
// Carchivum supports zip and tar. For tar, archiver also supports
// the following compression:
//
// When using archiver, compression is not optional.
//
package carchivum

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"
)

const (
	FmtUnsupported Format = iota
	FmtGzip
	FmtTar
	FmtTar1
	FmtTar2
	FmtZip
	FmtZipEmpty
	FmtZipSpanned
	FmtBzip2
	FmtLZH
	FmtLZW
	FmtRAR
	FmtRAROld
)

// Header information for common archive/compression formats.
// Zip includes: zip, jar, odt, ods, odp, docx, xlsx, pptx, apx, odf, ooxml
var (
	headerGzip []byte = []byte{0x1f, 0x8b}
	headerTar1 []byte = []byte{0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x30, 0x30} // offset: 257
	headerTar2 []byte = []byte{0x75, 0x73, 0x74, 0x61, 0x72, 0x00, 0x20, 0x00} // offset: 257
	headerZip []byte = []byte{0x50, 0x4b, 0x03, 0x04}
	headerZipEmpty []byte = []byte{0x50, 0x4b, 0x05, 0x06}
	headerZipSpanned []byte = []byte{0x50, 0x4b, 0x07, 0x08}
	headerBzip2 []byte = []byte{0x42, 0x5a, 0x68}
	headerLZH []byte = []byte{0x1F, 0xa0}
	headerLZW []byte = []byte{0x1F, 0x9d}
	headerRAR []byte = []byte{0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x01, 0x00}
	headerRAROld []byte = []byte{0x52, 0x61, 0x72, 0x21, 0x1a, 0x07, 0x00}
)

func GetFileFormat(r io.ReaderAt) (Format, error) {
	h := make([]byte, 8, 8)

	r.ReadAt(h, 0)

	if bytes.Equal(headerGzip, h[0:2]) {
		return FmtGzip, nil
	}

	if bytes.Equal(headerZip, h[0:4]) {
		return FmtZip, nil
	}

	r.ReadAt(h, 257)
	if bytes.Equal(headerTar1, h) || bytes.Equal(headerTar2, h) {
		return FmtTar, nil
	}

	// unsupported
	if bytes.Equal(headerRAROld, h) {
		return FmtUnsupported, FmtRAROld.NotSupportedError()
	}

	if bytes.Equal(headerRAR, h) {
		return FmtUnsupported, FmtRAR.NotSupportedError()
	}

	r.ReadAt(h, 0)
	if bytes.Equal(headerZipEmpty, h[0:4]) {
		return FmtUnsupported, FmtZipEmpty.NotSupportedError()
	}

	if bytes.Equal(headerZipSpanned, h[0:4]) {
		return FmtUnsupported, FmtZipSpanned.NotSupportedError()
	}

	if bytes.Equal(headerBzip2, h[0:3]) {
		return FmtUnsupported, FmtBzip2.NotSupportedError()
	}

	if bytes.Equal(headerLZW, h[0:2]) {
		return FmtUnsupported, FmtLZW.NotSupportedError()
	}

	if bytes.Equal(headerLZH, h[0:2]) {
		return FmtUnsupported, FmtLZH.NotSupportedError()
	}

	return FmtUnsupported, FmtUnsupported.NotSupportedError()
}

type (
	Format int
)

func (f Format) ToString() string {
	switch f {
	case FmtGzip:
		return "gzip"
	case FmtTar1, FmtTar2:
		return "tar"
	case FmtZip:
		return "zip"
	case FmtZipEmpty:
		return "empty zip archive"
	case FmtZipSpanned:
		return "spanned zip archive"
	case FmtBzip2:
		return "bzip2"
	case FmtLZH:
		return "LZH"
	case FmtLZW:
		return "LZW"
	case FmtRAR:
		return "RAR post 5.0"
	case FmtRAROld:
		return  "RAR pre 1.5"
	}
	return "unsupported"
}

func (f Format) NotSupportedError() error {
	switch f {
	case FmtZipEmpty:
		return fmt.Errorf("empty zip archive is not supported")
	case FmtZipSpanned:
		return fmt.Errorf("spanned zip archive is not supported")
	case FmtBzip2 :
		return fmt.Errorf("bzip2 is not supported")
	case FmtLZH:
		return fmt.Errorf("LZH is not supported")
	case FmtLZW:
		return fmt.Errorf("LZW is not supported")
	case FmtRAR:
		return fmt.Errorf("RAR post 5.0 is not supported")
	case FmtRAROld:
		return fmt.Errorf("RAR pre 1.5 is not supported")
	}
	return fmt.Errorf("unsupported format error, more specific information unavailable")
}

var defaultFormat Format = FmtGzip

// Options
var AppendDate bool
var TimeFormat string = time.RFC3339
var UseNano bool
var Separator string = "-"
var MakeUnique bool = false
// default max random number for random number generation.
var MaxRand = 10000

// we assume this count isn't going to change during runtime
var cpu int = runtime.NumCPU()

// Arbitrarily set the multiplier to some default value.
var CPUMultiplier int = 4

// Car is a Compressed Archive. The struct holds information about Cars and
// their processing.
type Car struct {
	// Name of the archive, this includes path information, if any.
	Name string
	UseLongExt bool
	UseFullpath bool

	// Create operation modifiers
	Owner int
	Group int
	Mode os.FileMode

	// Extract operation modifiers

	// Local file selection
	// List of files to delete if applicable.
	Delete []string
	DeleteFiles bool

	// Exclude file processing
	Exclude string
	ExcludeExt []string
	ExcludeExtCount int
	ExcludeAnchored string

	// Include file processing
	Include string
	IncludeExt []string
	IncludeExtCount int
	IncludeAnchored string

	Newer string
	NewerMTime string
	NewerFile string

	// Processing queue
	FileCh	chan *os.File

	// Other Counters
	counterLock sync.Mutex
	files int32
	bytes int64
	compressedBytes int64

	// timer
	t0 time.Time
	𝛥t float64
}

func (c *Car) setDelta() {
	c.𝛥t = float64(time.Since(c.t0)) / 1e9
}

func (c *Car) Delta() float64 {
	return c.𝛥t
}

func (c *Car) Message() string {
	return fmt.Sprintf("%q created in %4f seconds\n%d files totalling %d bytes were processed", c.Name, c.𝛥t, c.files, c.bytes)
}

// addFile  reads a file and pipes it to the zipper goroutine.
func (c *Car) AddFile(root, p string, fi os.FileInfo, err error) error {
	logger.Debugf("root: %s, p: %s, fi.Name: %s", root, p, fi.Name())

	// Check fileInfo to see if this should be added to archive
	process, err := c.filterFileInfo(fi)
	if err != nil {
		return err
	}
	if !process {
		return nil
	}

	// Check path information to see if this should be added to archive
	process, err = c.filterPaths(root, p)
	if err != nil {
		return err
	}
	if !process {
		return nil
	}

	var relPath string
	relPath, err = filepath.Rel(root, p)
	if err != nil {
		logger.Error(err)
		return err
	}

	if relPath == "," {
		logger.Debug("Don't add relative root")
		return nil
	}

	if !c.UseFullpath {
		p = filepath.Join(filepath.Base(root), relPath)
	}

	f, err := os.Open(p)
	if err != nil {
		logger.Error(err)
		return err
	}

	c.counterLock.Lock()
	c.files++
	c.bytes += fi.Size()
	c.counterLock.Unlock()

	c.FileCh <- f

	return nil
}

func (c *Car) filterFileInfo(fi os.FileInfo) (bool, error) {
	// Don't add symlinks, otherwise would have to code some cycle
	// detection amongst other stuff.
	if fi.Mode() & os.ModeSymlink == os.ModeSymlink {
		logger.Debugf("don't follow symlinks: %q", p)
		return nil
	}

}

func (c *Car) filterPath(root, p string) (bool, error) {
	if strings.HasSuffix(root, p) {
		logger.Debugf("%s | %s, don't add if source is the source directory", root, p)
		return false, nil
	}

	b, err := c.includeFile(root, p)
	if err != nil {
		return false, err
	}
	if !b {
		logger.Debugf("don't include %q", p)
		return false, nil
	}

	b, err = c.excludeFile(root, p)
	if err != nil {
		return false, err
	}
	if b {
		logger.Debugf("exclude %q", p)
		return false, nil
	}

	return true, nil
}

func (c *Car) includeFile(root, p string) (bool, error) {
	logger.Infof("%sroot: %s c.IncludeAnchored %s", root, p, c.IncludeAnchored)
	if c.IncludeAnchored != "" {
		logger.Info(filepath.Base(p))
		if strings.HasPrefix(filepath.Base(c.IncludeAnchored), p) {
			logger.Info("has prefix")
			return true, nil
		}
	}

	// since we are just evaluating a file, we use match and look at the
	// fullpath
	if c.Include != "" {
		matches, err := filepath.Match(c.Include, filepath.Join(root, p))
		if err != nil {
			return false, err
		}

		if matches {
			return true, nil
		}
	}

	if c.IncludeExtCount != 0 {
		for _, ext := range c.IncludeExt {
			if strings.HasSuffix(filepath.Base(p), "." + ext) {
				return true, nil
			}
		}
	}

	// time processing falls under include
	if c.NewerMTime != "" {

	}

	return false, nil
}

func (c *Car) excludeFile(root, p string) (bool, error) {
	logger.Infof("%s c.ExcludeAnchored %s", p, c.ExcludeAnchored)
	if c.ExcludeAnchored != "" {
		logger.Info(filepath.Base(p))
		if strings.HasPrefix(filepath.Base(p), c.ExcludeAnchored) {
			logger.Info("has prefix")
			return true, nil
		}
	}

	// since we are just evaluating a file, we use match and look at the
	// fullpath
	if c.Exclude != "" {
		matches, err := filepath.Match(c.Exclude, filepath.Join(root, p))
		if err != nil {
			return false, err
		}

		if matches {
			return true, nil
		}
	}

	if c.ExcludeExtCount != 0 {
		for _, ext := range c.ExcludeExt {
			if strings.HasSuffix(filepath.Base(p), "." + ext) {
				return true, nil
			}
		}
	}

	return false, nil
}

func ParseFormat(s string) (Format, error) {
	switch s {
	case "gzip", "tar.gz", "tgz":
		return FmtGzip, nil
	case "tar":
		return FmtTar, nil
	case "zip":
		return FmtZip, nil
	}

	return FmtUnsupported, FmtUnsupported.NotSupportedError()
}

func formattedNow() string {
	return time.Now().Local().Format(TimeFormat)
}

func getFileParts(s string) (dir, file, ext string, err error) {
	// see if there is path involved, if there is, get the last part of it
	dir, filename := filepath.Split(s)
	parts := strings.Split(filename, ".")
	l := len(parts)
	switch l {
	case 2:
		file := parts[0]
		ext := parts[1]
		return dir, file, ext, nil
	case 1:
		file := parts[0]
		return dir, file, ext, nil
	case 0:
		err := fmt.Errorf("no destination filename found in %s", s)
		logger.Error(err)
		return dir, file, ext, err
	default:
		// join all but the last parts together with a "."
		file := strings.Join(parts[0:l-1], ".")
		ext := parts[l-1]
		return dir, file, ext, nil
	}

	err = fmt.Errorf("unable to determine destination filename and extension")
	logger.Error(err)
	return dir, file, ext, err
}
<|endoftext|>


<commit_before>package controller_test

import (
	"testing"
	"time"

	"github.com/fabric8-services/fabric8-wit/account"
	"github.com/fabric8-services/fabric8-wit/app"
	"github.com/fabric8-services/fabric8-wit/app/test"
	. "github.com/fabric8-services/fabric8-wit/controller"
	"github.com/fabric8-services/fabric8-wit/gormtestsupport"
	"github.com/fabric8-services/fabric8-wit/resource"
	tf "github.com/fabric8-services/fabric8-wit/test/testfixture"

	testsupport "github.com/fabric8-services/fabric8-wit/test"
	"github.com/goadesign/goa"
	uuid "github.com/satori/go.uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
)

type TestSpaceAreaREST struct {
	gormtestsupport.DBTestSuite
	svcSpaceAreas  *goa.Service
	ctrlSpaceAreas *SpaceAreasController
}

func TestRunSpaceAreaREST(t *testing.T) {
	resource.Require(t, resource.Database)
	suite.Run(t, &TestSpaceAreaREST{DBTestSuite: gormtestsupport.NewDBTestSuite()})
}

func (rest *TestSpaceAreaREST) SetupTest() {
	rest.svcSpaceAreas, rest.ctrlSpaceAreas = rest.SecuredController()
}

func (rest *TestSpaceAreaREST) SecuredController() (*goa.Service, *SpaceAreasController) {
	svc := testsupport.ServiceAsUser("Space-Area-Service", testsupport.TestIdentity)
	return svc, NewSpaceAreasController(svc, rest.GormDB, rest.Configuration)
}

func (rest *TestSpaceAreaREST) SecuredAreasController() (*goa.Service, *AreaController) {
	svc := testsupport.ServiceAsUser("Area-Service", testsupport.TestIdentity)
	return svc, NewAreaController(svc, rest.GormDB, rest.Configuration)
}

func (rest *TestSpaceAreaREST) SecuredAreasControllerWithIdentity(idn *account.Identity) (*goa.Service, *AreaController) {
	svc := testsupport.ServiceAsUser("Area-Service-With-Identity", *idn)
	return svc, NewAreaController(svc, rest.GormDB, rest.Configuration)
}

func (rest *TestSpaceAreaREST) UnSecuredController() (*goa.Service, *SpaceAreasController) {
	svc := goa.New("Area-Service")
	return svc, NewSpaceAreasController(svc, rest.GormDB, rest.Configuration)
}

func searchInAreaSlice(searchKey uuid.UUID, areaList *app.AreaList) *app.Area {
	for i := 0; i < len(areaList.Data); i++ {
		if searchKey == *areaList.Data[i].ID {
			return areaList.Data[i]
		}
	}
	return nil
}

func assertSpaceAreas(t *testing.T, areaList *app.AreaList, createdAreaUuids []uuid.UUID) {
	assert.Len(t, areaList.Data, 3)
	for i := 0; i < len(createdAreaUuids); i++ {
		assert.NotNil(t, searchInAreaSlice(createdAreaUuids[i], areaList))
	}
}

func (rest *TestSpaceAreaREST) TestListAreasOK() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	createdAreaUuids := []uuid.UUID{
		fxt.Areas[0].ID,
		fxt.Areas[1].ID,
		fxt.Areas[2].ID,
	}
	// when
	res, areaList := test.ListSpaceAreasOK(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, nil, nil)
	// then
	assertSpaceAreas(rest.T(), areaList, createdAreaUuids)
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasOKUsingExpiredIfModifiedSinceHeader() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	createdAreaUuids := []uuid.UUID{
		fxt.Areas[0].ID,
		fxt.Areas[1].ID,
		fxt.Areas[2].ID,
	}
	// when
	ifModifiedSince := app.ToHTTPTime(parentArea.UpdatedAt.Add(-1 * time.Hour))
	res, areaList := test.ListSpaceAreasOK(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, &ifModifiedSince, nil)
	// then
	assertSpaceAreas(rest.T(), areaList, createdAreaUuids)
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasOKUsingExpiredIfNoneMatchHeader() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	createdAreaUuids := []uuid.UUID{
		fxt.Areas[0].ID,
		fxt.Areas[1].ID,
		fxt.Areas[2].ID,
	}
	// when
	ifNoneMatch := "foo"
	res, areaList := test.ListSpaceAreasOK(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, nil, &ifNoneMatch)
	// then
	assertSpaceAreas(rest.T(), areaList, createdAreaUuids)
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasNotModifiedUsingIfModifiedSinceHeader() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	// when
	ifModifiedSince := app.ToHTTPTime(fxt.Areas[2].UpdatedAt)
	res := test.ListSpaceAreasNotModified(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, &ifModifiedSince, nil)
	// then
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasNotModifiedUsingIfNoneMatchHeader() {
	// given
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	parentArea := fxt.Areas[0]
	// when
	ifNoneMatch := app.GenerateEntitiesTag([]app.ConditionalRequestEntity{
		fxt.Areas[0],
		fxt.Areas[1],
		fxt.Areas[2],
	})
	res := test.ListSpaceAreasNotModified(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, nil, &ifNoneMatch)
	// then
	assertResponseHeaders(rest.T(), res)
}
<commit_msg>Clean test entites created by space_areas_blackbox_test.go (#2157)<commit_after>package controller_test

import (
	"testing"
	"time"

	"github.com/fabric8-services/fabric8-wit/account"
	"github.com/fabric8-services/fabric8-wit/app"
	"github.com/fabric8-services/fabric8-wit/app/test"
	. "github.com/fabric8-services/fabric8-wit/controller"
	"github.com/fabric8-services/fabric8-wit/gormtestsupport"
	"github.com/fabric8-services/fabric8-wit/resource"
	tf "github.com/fabric8-services/fabric8-wit/test/testfixture"

	testsupport "github.com/fabric8-services/fabric8-wit/test"
	"github.com/goadesign/goa"
	uuid "github.com/satori/go.uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"
)

type TestSpaceAreaREST struct {
	gormtestsupport.DBTestSuite
	svcSpaceAreas  *goa.Service
	ctrlSpaceAreas *SpaceAreasController
}

func TestRunSpaceAreaREST(t *testing.T) {
	resource.Require(t, resource.Database)
	suite.Run(t, &TestSpaceAreaREST{DBTestSuite: gormtestsupport.NewDBTestSuite()})
}

func (rest *TestSpaceAreaREST) SetupTest() {
	rest.DBTestSuite.SetupTest()
	rest.svcSpaceAreas, rest.ctrlSpaceAreas = rest.SecuredController()
}

func (rest *TestSpaceAreaREST) SecuredController() (*goa.Service, *SpaceAreasController) {
	svc := testsupport.ServiceAsUser("Space-Area-Service", testsupport.TestIdentity)
	return svc, NewSpaceAreasController(svc, rest.GormDB, rest.Configuration)
}

func (rest *TestSpaceAreaREST) SecuredAreasController() (*goa.Service, *AreaController) {
	svc := testsupport.ServiceAsUser("Area-Service", testsupport.TestIdentity)
	return svc, NewAreaController(svc, rest.GormDB, rest.Configuration)
}

func (rest *TestSpaceAreaREST) SecuredAreasControllerWithIdentity(idn *account.Identity) (*goa.Service, *AreaController) {
	svc := testsupport.ServiceAsUser("Area-Service-With-Identity", *idn)
	return svc, NewAreaController(svc, rest.GormDB, rest.Configuration)
}

func (rest *TestSpaceAreaREST) UnSecuredController() (*goa.Service, *SpaceAreasController) {
	svc := goa.New("Area-Service")
	return svc, NewSpaceAreasController(svc, rest.GormDB, rest.Configuration)
}

func searchInAreaSlice(searchKey uuid.UUID, areaList *app.AreaList) *app.Area {
	for i := 0; i < len(areaList.Data); i++ {
		if searchKey == *areaList.Data[i].ID {
			return areaList.Data[i]
		}
	}
	return nil
}

func assertSpaceAreas(t *testing.T, areaList *app.AreaList, createdAreaUuids []uuid.UUID) {
	assert.Len(t, areaList.Data, 3)
	for i := 0; i < len(createdAreaUuids); i++ {
		assert.NotNil(t, searchInAreaSlice(createdAreaUuids[i], areaList))
	}
}

func (rest *TestSpaceAreaREST) TestListAreasOK() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	createdAreaUuids := []uuid.UUID{
		fxt.Areas[0].ID,
		fxt.Areas[1].ID,
		fxt.Areas[2].ID,
	}
	// when
	res, areaList := test.ListSpaceAreasOK(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, nil, nil)
	// then
	assertSpaceAreas(rest.T(), areaList, createdAreaUuids)
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasOKUsingExpiredIfModifiedSinceHeader() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	createdAreaUuids := []uuid.UUID{
		fxt.Areas[0].ID,
		fxt.Areas[1].ID,
		fxt.Areas[2].ID,
	}
	// when
	ifModifiedSince := app.ToHTTPTime(parentArea.UpdatedAt.Add(-1 * time.Hour))
	res, areaList := test.ListSpaceAreasOK(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, &ifModifiedSince, nil)
	// then
	assertSpaceAreas(rest.T(), areaList, createdAreaUuids)
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasOKUsingExpiredIfNoneMatchHeader() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	createdAreaUuids := []uuid.UUID{
		fxt.Areas[0].ID,
		fxt.Areas[1].ID,
		fxt.Areas[2].ID,
	}
	// when
	ifNoneMatch := "foo"
	res, areaList := test.ListSpaceAreasOK(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, nil, &ifNoneMatch)
	// then
	assertSpaceAreas(rest.T(), areaList, createdAreaUuids)
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasNotModifiedUsingIfModifiedSinceHeader() {
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	// given
	parentArea := fxt.Areas[0]
	// when
	ifModifiedSince := app.ToHTTPTime(fxt.Areas[2].UpdatedAt)
	res := test.ListSpaceAreasNotModified(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, &ifModifiedSince, nil)
	// then
	assertResponseHeaders(rest.T(), res)
}

func (rest *TestSpaceAreaREST) TestListAreasNotModifiedUsingIfNoneMatchHeader() {
	// given
	fxt := tf.NewTestFixture(rest.T(), rest.DB,
		tf.CreateWorkItemEnvironment(),
		tf.Areas(3, func(fxt *tf.TestFixture, idx int) error {
			if idx > 0 {
				fxt.Areas[idx].MakeChildOf(*fxt.Areas[idx-1])
			}
			return nil
		}),
	)
	parentArea := fxt.Areas[0]
	// when
	ifNoneMatch := app.GenerateEntitiesTag([]app.ConditionalRequestEntity{
		fxt.Areas[0],
		fxt.Areas[1],
		fxt.Areas[2],
	})
	res := test.ListSpaceAreasNotModified(rest.T(), rest.svcSpaceAreas.Context, rest.svcSpaceAreas, rest.ctrlSpaceAreas, parentArea.SpaceID, nil, &ifNoneMatch)
	// then
	assertResponseHeaders(rest.T(), res)
}
<|endoftext|>