<commit_before>package agent

import (
	"fmt"
	"reflect"
	"sync"
	"testing"
	"time"

	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/pkg"
	"github.com/coreos/fleet/registry"
	"github.com/coreos/fleet/unit"
)

func TestUpdateCache(t *testing.T) {
	name := "blah.service"
	mID := "mymachine"
	mach := &machine.FakeMachine{
		machine.MachineState{ID: mID},
	}
	us1 := &unit.UnitState{
		ActiveState: "active",
		UnitName:    name,
		MachineID:   mID,
	}
	us2 := &unit.UnitState{
		ActiveState: "inactive",
		UnitName:    name,
		MachineID:   mID,
	}
	ush1 := &unit.UnitStateHeartbeat{
		Name:  name,
		State: us1,
	}
	ush2 := &unit.UnitStateHeartbeat{
		Name:  name,
		State: us2,
	}
	ushNil := &unit.UnitStateHeartbeat{
		Name:  name,
		State: nil,
	}

	tests := []struct {
		ush         *unit.UnitStateHeartbeat
		cacheBefore map[string]*unit.UnitState
		cacheAfter  map[string]*unit.UnitState
		changed     bool
	}{
		{
			// new heartbeat should be saved
			ush1,
			map[string]*unit.UnitState{},
			map[string]*unit.UnitState{ush1.Name: us1},
			true,
		},
		{
			// nil heartbeat should not remove state from cache
			ushNil,
			map[string]*unit.UnitState{ush1.Name: us1},
			map[string]*unit.UnitState{ush1.Name: nil},
			true,
		},
		{
			// heartbeat different from one in cache should be saved
			ush2,
			map[string]*unit.UnitState{ush2.Name: us1},
			map[string]*unit.UnitState{ush2.Name: us2},
			true,
		},
		{
			// heartbeat same as one already in cache shouldn't be saved
			ush1,
			map[string]*unit.UnitState{ush1.Name: us1},
			map[string]*unit.UnitState{ush1.Name: us1},
			false,
		},
		{
			// non-nil heartbeat should overwrite existing nil
			ush1,
			map[string]*unit.UnitState{ush1.Name: nil},
			map[string]*unit.UnitState{ush1.Name: us1},
			true,
		},
		{
			// nil heartbeat should not overwrite existing nil
			ushNil,
			map[string]*unit.UnitState{ush1.Name: nil},
			map[string]*unit.UnitState{ush1.Name: nil},
			false,
		},
	}

	for i, tt := range tests {
		usp := NewUnitStatePublisher(nil, mach, 0)
		usp.cache = tt.cacheBefore
		changed := usp.updateCache(tt.ush)
		if tt.changed != changed {
			t.Errorf("case %d: expected changed=%t, got %t", i, tt.changed, changed)
		}
		if !reflect.DeepEqual(tt.cacheAfter, usp.cache) {
			t.Errorf("case %d: expected cache after operation %#v, got %#v", i, tt.cacheAfter, usp.cache)
		}
	}
}

func TestPruneCache(t *testing.T) {
	tests := []struct {
		cacheBefore map[string]*unit.UnitState
		cacheAfter  map[string]*unit.UnitState
	}{
		{
			cacheBefore: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
			},
			cacheAfter: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
			},
		},

		{
			cacheBefore: map[string]*unit.UnitState{
				"foo.service": nil,
			},
			cacheAfter: map[string]*unit.UnitState{},
		},
		{
			cacheBefore: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
				"bar.service": nil,
			},
			cacheAfter: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
			},
		},
	}

	for i, tt := range tests {
		mach := &machine.FakeMachine{
			machine.MachineState{ID: "XXX"},
		}
		usp := NewUnitStatePublisher(nil, mach, 0)
		usp.cache = tt.cacheBefore
		usp.pruneCache()
		if !reflect.DeepEqual(tt.cacheAfter, usp.cache) {
			t.Errorf("case %d: expected cache after operation %#v, got %#v", i, tt.cacheAfter, usp.cache)
		}
	}
}

func TestPurge(t *testing.T) {
	cache := map[string]*unit.UnitState{
		"foo.service": &unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "loaded",
		},
		"bar.service": nil,
	}
	initStates := []unit.UnitState{
		unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
		},
		unit.UnitState{
			UnitName:    "bar.service",
			ActiveState: "loaded",
		},
		unit.UnitState{
			UnitName:    "baz.service",
			ActiveState: "inactive",
		},
	}
	want := []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "baz.service",
			ActiveState: "inactive",
		},
	}
	freg := registry.NewFakeRegistry()
	freg.SetUnitStates(initStates)
	usp := NewUnitStatePublisher(freg, &machine.FakeMachine{}, 0)
	usp.cache = cache

	usp.Purge()

	us, err := freg.UnitStates()
	if err != nil {
		t.Errorf("unexpected error retrieving unit states: %v", err)
	} else if !reflect.DeepEqual(us, want) {
		msg := fmt.Sprintln("received unexpected unit states")
		msg += fmt.Sprintf("got: %#v\n", us)
		msg += fmt.Sprintf("want: %#v\n", want)
		t.Error(msg)
	}
}

func TestPublishOne(t *testing.T) {
	testCases := []struct {
		name       string
		state      *unit.UnitState
		initStates []unit.UnitState
		want       []*unit.UnitState
	}{
		// Simplest case - success
		{
			"foo.service",
			&unit.UnitState{
				UnitName:    "foo.service",
				ActiveState: "active",
				MachineID:   "xyz",
			},
			nil,
			[]*unit.UnitState{
				&unit.UnitState{
					UnitName:    "foo.service",
					ActiveState: "active",
					MachineID:   "xyz",
				},
			},
		},
		// Unit state with no machine ID is refused
		{
			"foo.service",
			&unit.UnitState{
				UnitName:    "foo.service",
				ActiveState: "active",
			},
			nil,
			[]*unit.UnitState{},
		},
		// Destroying existing unit state
		{
			"foo.service",
			nil,
			[]unit.UnitState{
				unit.UnitState{
					UnitName:    "foo.service",
					ActiveState: "active",
				},
			},
			[]*unit.UnitState{},
		},
		// Destroying non-existent unit state should not fail
		{
			"foo.service",
			nil,
			[]unit.UnitState{},
			[]*unit.UnitState{},
		},
	}

	for i, tt := range testCases {
		freg := registry.NewFakeRegistry()
		freg.SetUnitStates(tt.initStates)
		usp := NewUnitStatePublisher(freg, &machine.FakeMachine{}, 0)
		usp.publishOne(tt.name, tt.state)
		us, err := freg.UnitStates()
		if err != nil {
			t.Errorf("case %d: unexpected error retrieving unit states: %v", err)
			continue
		}
		if !reflect.DeepEqual(us, tt.want) {
			msg := fmt.Sprintf("case %d: received unexpected unit states\n", i)
			msg += fmt.Sprintf("got: %#v\n", us)
			msg += fmt.Sprintf("want: %#v\n", tt.want)
			t.Error(msg)
		}
	}
}

func TestUnitStatePublisherRun(t *testing.T) {
	fclock := &pkg.FakeClock{}
	states := make([]*unit.UnitState, 0)
	published := make(chan struct{})
	pf := func(name string, us *unit.UnitState) {
		states = append(states, us)
		go func() {
			published <- struct{}{}
		}()
	}
	usp := &UnitStatePublisher{
		mach:      &machine.FakeMachine{},
		publisher: pf,
		ttl:       5 * time.Second,
		mutex:     sync.RWMutex{},
		cache:     make(map[string]*unit.UnitState),
		clock:     fclock,
	}
	usp.cache = map[string]*unit.UnitState{
		"foo.service": &unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
			MachineID:   "XXX",
		},
	}

	bc := make(chan *unit.UnitStateHeartbeat)
	sc := make(chan bool)
	go func() {
		usp.Run(bc, sc)
	}()

	// block until Run() is definitely waiting on After()
	// TODO(jonboulle): do this more elegantly!!!
	for {
		if fclock.Sleepers() == 1 {
			break
		}
	}

	// tick less than the publish interval
	fclock.Tick(time.Second)

	select {
	case <-published:
		t.Fatal("UnitState published unexpectedly!")
	default:
	}
	want := []*unit.UnitState{}
	if !reflect.DeepEqual(states, want) {
		t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
	}

	// now up to the publish interval
	fclock.Tick(4 * time.Second)
	want = []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
			MachineID:   "XXX",
		},
	}
	select {
	case <-published:
		if !reflect.DeepEqual(states, want) {
			t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
		}
	case <-time.After(time.Second):
		t.Fatalf("UnitState not published as expected!")
	}

	// reset states
	states = []*unit.UnitState{}

	// tick less than the publish interval, again
	fclock.Tick(4 * time.Second)

	// no more should be published
	select {
	case <-published:
		t.Fatal("UnitState published unexpectedly!")
	default:
	}
	want = []*unit.UnitState{}
	if !reflect.DeepEqual(states, want) {
		t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
	}

	// tick way past the publish interval
	fclock.Tick(time.Hour)
	want = []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
			MachineID:   "XXX",
		},
	}

	// state should be published, but just once (since it's still just a single event)
	select {
	case <-published:
		if !reflect.DeepEqual(states, want) {
			t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
		}
	case <-time.After(time.Second):
		t.Fatalf("UnitState not published as expected!")
	}
}
<commit_msg>agent: test after stopping UnitStatePublisher too<commit_after>package agent

import (
	"fmt"
	"reflect"
	"sync"
	"testing"
	"time"

	"github.com/coreos/fleet/machine"
	"github.com/coreos/fleet/pkg"
	"github.com/coreos/fleet/registry"
	"github.com/coreos/fleet/unit"
)

func TestUpdateCache(t *testing.T) {
	name := "blah.service"
	mID := "mymachine"
	mach := &machine.FakeMachine{
		machine.MachineState{ID: mID},
	}
	us1 := &unit.UnitState{
		ActiveState: "active",
		UnitName:    name,
		MachineID:   mID,
	}
	us2 := &unit.UnitState{
		ActiveState: "inactive",
		UnitName:    name,
		MachineID:   mID,
	}
	ush1 := &unit.UnitStateHeartbeat{
		Name:  name,
		State: us1,
	}
	ush2 := &unit.UnitStateHeartbeat{
		Name:  name,
		State: us2,
	}
	ushNil := &unit.UnitStateHeartbeat{
		Name:  name,
		State: nil,
	}

	tests := []struct {
		ush         *unit.UnitStateHeartbeat
		cacheBefore map[string]*unit.UnitState
		cacheAfter  map[string]*unit.UnitState
		changed     bool
	}{
		{
			// new heartbeat should be saved
			ush1,
			map[string]*unit.UnitState{},
			map[string]*unit.UnitState{ush1.Name: us1},
			true,
		},
		{
			// nil heartbeat should not remove state from cache
			ushNil,
			map[string]*unit.UnitState{ush1.Name: us1},
			map[string]*unit.UnitState{ush1.Name: nil},
			true,
		},
		{
			// heartbeat different from one in cache should be saved
			ush2,
			map[string]*unit.UnitState{ush2.Name: us1},
			map[string]*unit.UnitState{ush2.Name: us2},
			true,
		},
		{
			// heartbeat same as one already in cache shouldn't be saved
			ush1,
			map[string]*unit.UnitState{ush1.Name: us1},
			map[string]*unit.UnitState{ush1.Name: us1},
			false,
		},
		{
			// non-nil heartbeat should overwrite existing nil
			ush1,
			map[string]*unit.UnitState{ush1.Name: nil},
			map[string]*unit.UnitState{ush1.Name: us1},
			true,
		},
		{
			// nil heartbeat should not overwrite existing nil
			ushNil,
			map[string]*unit.UnitState{ush1.Name: nil},
			map[string]*unit.UnitState{ush1.Name: nil},
			false,
		},
	}

	for i, tt := range tests {
		usp := NewUnitStatePublisher(nil, mach, 0)
		usp.cache = tt.cacheBefore
		changed := usp.updateCache(tt.ush)
		if tt.changed != changed {
			t.Errorf("case %d: expected changed=%t, got %t", i, tt.changed, changed)
		}
		if !reflect.DeepEqual(tt.cacheAfter, usp.cache) {
			t.Errorf("case %d: expected cache after operation %#v, got %#v", i, tt.cacheAfter, usp.cache)
		}
	}
}

func TestPruneCache(t *testing.T) {
	tests := []struct {
		cacheBefore map[string]*unit.UnitState
		cacheAfter  map[string]*unit.UnitState
	}{
		{
			cacheBefore: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
			},
			cacheAfter: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
			},
		},

		{
			cacheBefore: map[string]*unit.UnitState{
				"foo.service": nil,
			},
			cacheAfter: map[string]*unit.UnitState{},
		},
		{
			cacheBefore: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
				"bar.service": nil,
			},
			cacheAfter: map[string]*unit.UnitState{
				"foo.service": &unit.UnitState{},
			},
		},
	}

	for i, tt := range tests {
		mach := &machine.FakeMachine{
			machine.MachineState{ID: "XXX"},
		}
		usp := NewUnitStatePublisher(nil, mach, 0)
		usp.cache = tt.cacheBefore
		usp.pruneCache()
		if !reflect.DeepEqual(tt.cacheAfter, usp.cache) {
			t.Errorf("case %d: expected cache after operation %#v, got %#v", i, tt.cacheAfter, usp.cache)
		}
	}
}

func TestPurge(t *testing.T) {
	cache := map[string]*unit.UnitState{
		"foo.service": &unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "loaded",
		},
		"bar.service": nil,
	}
	initStates := []unit.UnitState{
		unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
		},
		unit.UnitState{
			UnitName:    "bar.service",
			ActiveState: "loaded",
		},
		unit.UnitState{
			UnitName:    "baz.service",
			ActiveState: "inactive",
		},
	}
	want := []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "baz.service",
			ActiveState: "inactive",
		},
	}
	freg := registry.NewFakeRegistry()
	freg.SetUnitStates(initStates)
	usp := NewUnitStatePublisher(freg, &machine.FakeMachine{}, 0)
	usp.cache = cache

	usp.Purge()

	us, err := freg.UnitStates()
	if err != nil {
		t.Errorf("unexpected error retrieving unit states: %v", err)
	} else if !reflect.DeepEqual(us, want) {
		msg := fmt.Sprintln("received unexpected unit states")
		msg += fmt.Sprintf("got: %#v\n", us)
		msg += fmt.Sprintf("want: %#v\n", want)
		t.Error(msg)
	}
}

func TestPublishOne(t *testing.T) {
	testCases := []struct {
		name       string
		state      *unit.UnitState
		initStates []unit.UnitState
		want       []*unit.UnitState
	}{
		// Simplest case - success
		{
			"foo.service",
			&unit.UnitState{
				UnitName:    "foo.service",
				ActiveState: "active",
				MachineID:   "xyz",
			},
			nil,
			[]*unit.UnitState{
				&unit.UnitState{
					UnitName:    "foo.service",
					ActiveState: "active",
					MachineID:   "xyz",
				},
			},
		},
		// Unit state with no machine ID is refused
		{
			"foo.service",
			&unit.UnitState{
				UnitName:    "foo.service",
				ActiveState: "active",
			},
			nil,
			[]*unit.UnitState{},
		},
		// Destroying existing unit state
		{
			"foo.service",
			nil,
			[]unit.UnitState{
				unit.UnitState{
					UnitName:    "foo.service",
					ActiveState: "active",
				},
			},
			[]*unit.UnitState{},
		},
		// Destroying non-existent unit state should not fail
		{
			"foo.service",
			nil,
			[]unit.UnitState{},
			[]*unit.UnitState{},
		},
	}

	for i, tt := range testCases {
		freg := registry.NewFakeRegistry()
		freg.SetUnitStates(tt.initStates)
		usp := NewUnitStatePublisher(freg, &machine.FakeMachine{}, 0)
		usp.publishOne(tt.name, tt.state)
		us, err := freg.UnitStates()
		if err != nil {
			t.Errorf("case %d: unexpected error retrieving unit states: %v", err)
			continue
		}
		if !reflect.DeepEqual(us, tt.want) {
			msg := fmt.Sprintf("case %d: received unexpected unit states\n", i)
			msg += fmt.Sprintf("got: %#v\n", us)
			msg += fmt.Sprintf("want: %#v\n", tt.want)
			t.Error(msg)
		}
	}
}

func TestUnitStatePublisherRun(t *testing.T) {
	fclock := &pkg.FakeClock{}
	states := make([]*unit.UnitState, 0)
	published := make(chan struct{})
	pf := func(name string, us *unit.UnitState) {
		states = append(states, us)
		go func() {
			published <- struct{}{}
		}()
	}
	usp := &UnitStatePublisher{
		mach:      &machine.FakeMachine{},
		publisher: pf,
		ttl:       5 * time.Second,
		mutex:     sync.RWMutex{},
		cache:     make(map[string]*unit.UnitState),
		clock:     fclock,
	}
	usp.cache = map[string]*unit.UnitState{
		"foo.service": &unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
			MachineID:   "XXX",
		},
	}

	bc := make(chan *unit.UnitStateHeartbeat)
	sc := make(chan bool)
	go func() {
		usp.Run(bc, sc)
	}()

	// block until Run() is definitely waiting on After()
	// TODO(jonboulle): do this more elegantly!!!
	for {
		if fclock.Sleepers() == 1 {
			break
		}
	}

	// tick less than the publish interval
	fclock.Tick(time.Second)

	select {
	case <-published:
		t.Fatal("UnitState published unexpectedly!")
	default:
	}
	want := []*unit.UnitState{}
	if !reflect.DeepEqual(states, want) {
		t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
	}

	// now up to the publish interval
	fclock.Tick(4 * time.Second)
	want = []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
			MachineID:   "XXX",
		},
	}
	select {
	case <-published:
		if !reflect.DeepEqual(states, want) {
			t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
		}
	case <-time.After(time.Second):
		t.Fatalf("UnitState not published as expected!")
	}

	// reset states
	states = []*unit.UnitState{}

	// block until Run() is definitely waiting on After()
	// TODO(jonboulle): do this more elegantly!!!
	for {
		if fclock.Sleepers() == 1 {
			break
		}
	}

	// tick less than the publish interval, again
	fclock.Tick(4 * time.Second)

	// no more should be published
	select {
	case <-published:
		t.Fatal("UnitState published unexpectedly!")
	default:
	}
	want = []*unit.UnitState{}
	if !reflect.DeepEqual(states, want) {
		t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
	}

	// tick way past the publish interval
	fclock.Tick(time.Hour)
	want = []*unit.UnitState{
		&unit.UnitState{
			UnitName:    "foo.service",
			ActiveState: "active",
			MachineID:   "XXX",
		},
	}

	// state should be published, but just once (since it's still just a single event)
	select {
	case <-published:
		if !reflect.DeepEqual(states, want) {
			t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
		}
	case <-time.After(time.Second):
		t.Fatalf("UnitState not published as expected!")
	}

	// now stop the UnitStatePublisher
	close(sc)

	// reset states
	states = []*unit.UnitState{}

	// block until Run() is definitely waiting on After()
	// TODO(jonboulle): do this more elegantly!!!
	for {
		if fclock.Sleepers() == 1 {
			break
		}
	}

	// tick way past the publish interval
	fclock.Tick(time.Hour)

	// no more states should be published
	select {
	case <-published:
		t.Fatal("UnitState published unexpectedly!")
	default:
	}
	want = []*unit.UnitState{}
	if !reflect.DeepEqual(states, want) {
		t.Errorf("bad UnitStates: got %#v, want %#v", states, want)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"

	"github.com/spf13/cobra"
)

// NewCmdGet creates a command object for the generic "get" action, which
// retrieves one or more resources from a server.
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "get [(-o|--output=)json|yaml|...] <resource> [<id>]",
		Short: "Display one or many resources",
		Long: `Display one or many resources.

Possible resources include pods (po), replication controllers (rc), services
(se), minions (mi), or events (ev).

If you specify a Go template, you can use any fields defined for the API version
you are connecting to the server with.

Examples:
  $ kubectl get pods
  <list all pods in ps output format>

  $ kubectl get replicationController 1234-56-7890-234234-456456
  <list single replication controller in ps output format>

  $ kubectl get -o json pod 1234-56-7890-234234-456456
  <list single pod in json output format>

  $ kubectl get rc,services
  <list replication controllers and services together in ps output format>`,
		Run: func(cmd *cobra.Command, args []string) {
			RunGet(f, out, cmd, args)
		},
	}
	cmd.Flags().StringP("output", "o", "", "Output format: json|yaml|template|templatefile")
	cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version)")
	cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers")
	cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
	cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.")
	cmd.Flags().Bool("watch-only", false, "Watch for changes to the requseted object(s), without listing/getting first.")
	return cmd
}

// RunGet implements the generic Get command
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
// TODO: return an error instead of using glog.Fatal and checkErr
func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
	selector := GetFlagString(cmd, "selector")
	mapper, typer := f.Object(cmd)

	// handle watch separately since we cannot watch multiple resource types
	isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only")
	if isWatch || isWatchOnly {
		r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
			NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
			SelectorParam(selector).
			ResourceTypeOrNameArgs(args...).
			SingleResourceType().
			Do()

		mapping, err := r.ResourceMapping()
		checkErr(err)

		printer, err := printerForMapping(f, cmd, mapping)
		checkErr(err)

		obj, err := r.Object()
		checkErr(err)

		rv, err := mapping.MetadataAccessor.ResourceVersion(obj)
		checkErr(err)

		// print the current object
		if !isWatchOnly {
			if err := printer.PrintObj(obj, out); err != nil {
				checkErr(fmt.Errorf("unable to output the provided object: %v", err))
			}
		}

		// print watched changes
		w, err := r.Watch(rv)
		checkErr(err)

		kubectl.WatchLoop(w, func(e watch.Event) error {
			return printer.PrintObj(e.Object, out)
		})
		return
	}

	printer, generic, err := printerForCommand(cmd)
	checkErr(err)

	b := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
		NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
		SelectorParam(selector).
		ResourceTypeOrNameArgs(args...).
		Latest()

	if generic {
		// the outermost object will be converted to the output-version
		printer = kubectl.NewVersionedPrinter(printer, api.Scheme, outputVersion(cmd))

		obj, err := b.Flatten().Do().Object()
		checkErr(err)

		err = printer.PrintObj(obj, out)
		checkErr(err)
		return
	}

	// use the default printer for each object
	err = b.Do().Visit(func(r *resource.Info) error {
		printer, err := printerForMapping(f, cmd, r.Mapping)
		if err != nil {
			return err
		}
		return printer.PrintObj(r.Object, out)
	})
	checkErr(err)
}

// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
func outputVersion(cmd *cobra.Command) string {
	outputVersion := GetFlagString(cmd, "output-version")
	if len(outputVersion) == 0 {
		outputVersion = GetFlagString(cmd, "api-version")
	}
	return outputVersion
}

// printerForCommand returns the default printer for this command.
func printerForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) {
	outputFormat := GetFlagString(cmd, "output")
	templateFile := GetFlagString(cmd, "template")
	if len(outputFormat) == 0 && len(templateFile) != 0 {
		outputFormat = "template"
	}

	return kubectl.GetPrinter(outputFormat, templateFile)
}

// printerForMapping returns a printer suitable for displaying the provided resource type.
func printerForMapping(f *Factory, cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
	printer, ok, err := printerForCommand(cmd)
	if err != nil {
		return nil, err
	}
	if ok {
		printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, outputVersion(cmd))
	} else {
		printer, err = f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers"))
		if err != nil {
			return nil, err
		}
	}
	return printer, nil
}
<commit_msg>The get command should properly handle users providing invalid apiversions<commit_after>/*
Copyright 2014 Google Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package cmd

import (
	"fmt"
	"io"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"

	"github.com/spf13/cobra"
)

// NewCmdGet creates a command object for the generic "get" action, which
// retrieves one or more resources from a server.
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "get [(-o|--output=)json|yaml|...] <resource> [<id>]",
		Short: "Display one or many resources",
		Long: `Display one or many resources.

Possible resources include pods (po), replication controllers (rc), services
(se), minions (mi), or events (ev).

If you specify a Go template, you can use any fields defined for the API version
you are connecting to the server with.

Examples:
  $ kubectl get pods
  <list all pods in ps output format>

  $ kubectl get replicationController 1234-56-7890-234234-456456
  <list single replication controller in ps output format>

  $ kubectl get -o json pod 1234-56-7890-234234-456456
  <list single pod in json output format>

  $ kubectl get rc,services
  <list replication controllers and services together in ps output format>`,
		Run: func(cmd *cobra.Command, args []string) {
			RunGet(f, out, cmd, args)
		},
	}
	cmd.Flags().StringP("output", "o", "", "Output format: json|yaml|template|templatefile")
	cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version)")
	cmd.Flags().Bool("no-headers", false, "When using the default output, don't print headers")
	cmd.Flags().StringP("template", "t", "", "Template string or path to template file to use when -o=template or -o=templatefile.")
	cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
	cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.")
	cmd.Flags().Bool("watch-only", false, "Watch for changes to the requseted object(s), without listing/getting first.")
	return cmd
}

// RunGet implements the generic Get command
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd
// TODO: return an error instead of using glog.Fatal and checkErr
func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
	selector := GetFlagString(cmd, "selector")
	mapper, typer := f.Object(cmd)

	// handle watch separately since we cannot watch multiple resource types
	isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only")
	if isWatch || isWatchOnly {
		r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
			NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
			SelectorParam(selector).
			ResourceTypeOrNameArgs(args...).
			SingleResourceType().
			Do()

		mapping, err := r.ResourceMapping()
		checkErr(err)

		printer, err := printerForMapping(f, cmd, mapping)
		checkErr(err)

		obj, err := r.Object()
		checkErr(err)

		rv, err := mapping.MetadataAccessor.ResourceVersion(obj)
		checkErr(err)

		// print the current object
		if !isWatchOnly {
			if err := printer.PrintObj(obj, out); err != nil {
				checkErr(fmt.Errorf("unable to output the provided object: %v", err))
			}
		}

		// print watched changes
		w, err := r.Watch(rv)
		checkErr(err)

		kubectl.WatchLoop(w, func(e watch.Event) error {
			return printer.PrintObj(e.Object, out)
		})
		return
	}

	printer, generic, err := printerForCommand(cmd)
	checkErr(err)

	b := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
		NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
		SelectorParam(selector).
		ResourceTypeOrNameArgs(args...).
		Latest()

	if generic {
		// the outermost object will be converted to the output-version
		version := outputVersion(cmd)
		if len(version) == 0 {
			// TODO: add a new ResourceBuilder mode for Object() that attempts to ensure the objects
			// are in the appropriate version if one exists (and if not, use the best effort).
			// TODO: ensure api-version is set with the default preferred api version by the client
			// builder on initialization
			version = latest.Version
		}
		printer = kubectl.NewVersionedPrinter(printer, api.Scheme, version)

		obj, err := b.Flatten().Do().Object()
		checkErr(err)

		err = printer.PrintObj(obj, out)
		checkErr(err)
		return
	}

	// use the default printer for each object
	err = b.Do().Visit(func(r *resource.Info) error {
		printer, err := printerForMapping(f, cmd, r.Mapping)
		if err != nil {
			return err
		}
		return printer.PrintObj(r.Object, out)
	})
	checkErr(err)
}

// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
func outputVersion(cmd *cobra.Command) string {
	outputVersion := GetFlagString(cmd, "output-version")
	if len(outputVersion) == 0 {
		outputVersion = GetFlagString(cmd, "api-version")
	}
	return outputVersion
}

// printerForCommand returns the default printer for this command.
func printerForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) {
	outputFormat := GetFlagString(cmd, "output")
	templateFile := GetFlagString(cmd, "template")
	if len(outputFormat) == 0 && len(templateFile) != 0 {
		outputFormat = "template"
	}

	return kubectl.GetPrinter(outputFormat, templateFile)
}

// printerForMapping returns a printer suitable for displaying the provided resource type.
func printerForMapping(f *Factory, cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
	printer, ok, err := printerForCommand(cmd)
	if err != nil {
		return nil, err
	}
	if ok {
		version := outputVersion(cmd)
		if len(version) == 0 {
			version = mapping.APIVersion
		}
		if len(version) == 0 {
			return nil, fmt.Errorf("you must specify an output-version when using this output format")
		}
		printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version)
	} else {
		printer, err = f.Printer(cmd, mapping, GetFlagBool(cmd, "no-headers"))
		if err != nil {
			return nil, err
		}
	}
	return printer, nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package aggregator

import (
	"errors"
	"sync"
	"sync/atomic"
	"time"

	"github.com/m3db/m3metrics/metric/unaggregated"
	"github.com/m3db/m3metrics/policy"
	"github.com/m3db/m3x/clock"
	"github.com/m3db/m3x/instrument"

	"github.com/uber-go/tally"
)

var (
	errAggregatorClosed = errors.New("aggregator is closed")
)

type aggregatorMetrics struct {
	addMetricWithPolicies instrument.MethodMetrics
	tickDuration          tally.Timer
	tickExpired           tally.Counter
}

func newAggregatorMetrics(scope tally.Scope, samplingRate float64) aggregatorMetrics {
	tickScope := scope.SubScope("tick")
	return aggregatorMetrics{
		addMetricWithPolicies: instrument.NewMethodMetrics(scope, "addMetricsWithPolicies", samplingRate),
		tickDuration:          tickScope.Timer("duration"),
		tickExpired:           tickScope.Counter("expired"),
	}
}

type addMetricWithPoliciesFn func(
	mu unaggregated.MetricUnion,
	policies policy.VersionedPolicies,
) error

type waitForFn func(d time.Duration) <-chan time.Time

// aggregator stores aggregations of different types of metrics (e.g., counter,
// timer, gauges) and periodically flushes them out.
type aggregator struct {
	opts                    Options
	nowFn                   clock.NowFn
	checkInterval           time.Duration
	closed                  int32
	doneCh                  chan struct{}
	wg                      sync.WaitGroup
	metricMap               *metricMap
	addMetricWithPoliciesFn addMetricWithPoliciesFn
	waitForFn               waitForFn
	metrics                 aggregatorMetrics
}

// NewAggregator creates a new aggregator.
func NewAggregator(opts Options) Aggregator {
	doneCh := make(chan struct{})
	iOpts := opts.InstrumentOptions()
	agg := &aggregator{
		opts:          opts,
		nowFn:         opts.ClockOptions().NowFn(),
		checkInterval: opts.EntryCheckInterval(),
		doneCh:        doneCh,
		metricMap:     newMetricMap(opts),
		metrics:       newAggregatorMetrics(iOpts.MetricsScope(), iOpts.MetricsSamplingRate()),
	}
	agg.addMetricWithPoliciesFn = agg.metricMap.AddMetricWithPolicies
	agg.waitForFn = time.After

	if agg.checkInterval > 0 {
		agg.wg.Add(1)
		go agg.tick()
	}

	agg.wg.Add(1)
	go agg.reportMetrics()

	return agg
}

func (agg *aggregator) AddMetricWithPolicies(
	mu unaggregated.MetricUnion,
	policies policy.VersionedPolicies,
) error {
	callStart := agg.nowFn()
	if atomic.LoadInt32(&agg.closed) == 1 {
		agg.metrics.addMetricWithPolicies.ReportError(agg.nowFn().Sub(callStart))
		return errAggregatorClosed
	}
	err := agg.addMetricWithPoliciesFn(mu, policies)
	agg.metrics.addMetricWithPolicies.ReportSuccessOrError(err, agg.nowFn().Sub(callStart))
	return err
}

func (agg *aggregator) Close() {
	if !atomic.CompareAndSwapInt32(&agg.closed, 0, 1) {
		return
	}

	// Waiting for the ticking goroutine to return.
	close(agg.doneCh)
	agg.wg.Wait()

	// Closing metric map.
	agg.metricMap.Close()
}

func (agg *aggregator) tick() {
	defer agg.wg.Done()

	for {
		select {
		case <-agg.doneCh:
			return
		default:
			agg.tickInternal()
		}
	}
}

func (agg *aggregator) tickInternal() {
	start := agg.nowFn()
	tickExpired := agg.metricMap.DeleteExpired(agg.checkInterval)
	tickDuration := agg.nowFn().Sub(start)
	agg.metrics.tickExpired.Inc(tickExpired)
	agg.metrics.tickDuration.Record(tickDuration)
	if tickDuration < agg.checkInterval {
		// NB(xichen): use a channel here instead of sleeping in case
		// server needs to close and we don't tick frequently enough.
		select {
		case <-agg.waitForFn(agg.checkInterval - tickDuration):
		case <-agg.doneCh:
		}
	}
}

func (agg *aggregator) reportMetrics() {
	defer agg.wg.Done()

	reportInterval := agg.opts.InstrumentOptions().ReportInterval()
	t := time.NewTicker(reportInterval)

	for {
		select {
		case <-t.C:
			agg.metricMap.reportMetrics()
		case <-agg.doneCh:
			t.Stop()
			return
		}
	}
}
<commit_msg>Fix typo on the metric name<commit_after>// Copyright (c) 2016 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package aggregator

import (
	"errors"
	"sync"
	"sync/atomic"
	"time"

	"github.com/m3db/m3metrics/metric/unaggregated"
	"github.com/m3db/m3metrics/policy"
	"github.com/m3db/m3x/clock"
	"github.com/m3db/m3x/instrument"

	"github.com/uber-go/tally"
)

var (
	errAggregatorClosed = errors.New("aggregator is closed")
)

type aggregatorMetrics struct {
	addMetricWithPolicies instrument.MethodMetrics
	tickDuration          tally.Timer
	tickExpired           tally.Counter
}

func newAggregatorMetrics(scope tally.Scope, samplingRate float64) aggregatorMetrics {
	tickScope := scope.SubScope("tick")
	return aggregatorMetrics{
		addMetricWithPolicies: instrument.NewMethodMetrics(scope, "addMetricWithPolicies", samplingRate),
		tickDuration:          tickScope.Timer("duration"),
		tickExpired:           tickScope.Counter("expired"),
	}
}

type addMetricWithPoliciesFn func(
	mu unaggregated.MetricUnion,
	policies policy.VersionedPolicies,
) error

type waitForFn func(d time.Duration) <-chan time.Time

// aggregator stores aggregations of different types of metrics (e.g., counter,
// timer, gauges) and periodically flushes them out.
type aggregator struct {
	opts                    Options
	nowFn                   clock.NowFn
	checkInterval           time.Duration
	closed                  int32
	doneCh                  chan struct{}
	wg                      sync.WaitGroup
	metricMap               *metricMap
	addMetricWithPoliciesFn addMetricWithPoliciesFn
	waitForFn               waitForFn
	metrics                 aggregatorMetrics
}

// NewAggregator creates a new aggregator.
func NewAggregator(opts Options) Aggregator {
	doneCh := make(chan struct{})
	iOpts := opts.InstrumentOptions()
	agg := &aggregator{
		opts:          opts,
		nowFn:         opts.ClockOptions().NowFn(),
		checkInterval: opts.EntryCheckInterval(),
		doneCh:        doneCh,
		metricMap:     newMetricMap(opts),
		metrics:       newAggregatorMetrics(iOpts.MetricsScope(), iOpts.MetricsSamplingRate()),
	}
	agg.addMetricWithPoliciesFn = agg.metricMap.AddMetricWithPolicies
	agg.waitForFn = time.After

	if agg.checkInterval > 0 {
		agg.wg.Add(1)
		go agg.tick()
	}

	agg.wg.Add(1)
	go agg.reportMetrics()

	return agg
}

func (agg *aggregator) AddMetricWithPolicies(
	mu unaggregated.MetricUnion,
	policies policy.VersionedPolicies,
) error {
	callStart := agg.nowFn()
	if atomic.LoadInt32(&agg.closed) == 1 {
		agg.metrics.addMetricWithPolicies.ReportError(agg.nowFn().Sub(callStart))
		return errAggregatorClosed
	}
	err := agg.addMetricWithPoliciesFn(mu, policies)
	agg.metrics.addMetricWithPolicies.ReportSuccessOrError(err, agg.nowFn().Sub(callStart))
	return err
}

func (agg *aggregator) Close() {
	if !atomic.CompareAndSwapInt32(&agg.closed, 0, 1) {
		return
	}

	// Waiting for the ticking goroutine to return.
	close(agg.doneCh)
	agg.wg.Wait()

	// Closing metric map.
	agg.metricMap.Close()
}

func (agg *aggregator) tick() {
	defer agg.wg.Done()

	for {
		select {
		case <-agg.doneCh:
			return
		default:
			agg.tickInternal()
		}
	}
}

func (agg *aggregator) tickInternal() {
	start := agg.nowFn()
	tickExpired := agg.metricMap.DeleteExpired(agg.checkInterval)
	tickDuration := agg.nowFn().Sub(start)
	agg.metrics.tickExpired.Inc(tickExpired)
	agg.metrics.tickDuration.Record(tickDuration)
	if tickDuration < agg.checkInterval {
		// NB(xichen): use a channel here instead of sleeping in case
		// server needs to close and we don't tick frequently enough.
		select {
		case <-agg.waitForFn(agg.checkInterval - tickDuration):
		case <-agg.doneCh:
		}
	}
}

func (agg *aggregator) reportMetrics() {
	defer agg.wg.Done()

	reportInterval := agg.opts.InstrumentOptions().ReportInterval()
	t := time.NewTicker(reportInterval)

	for {
		select {
		case <-t.C:
			agg.metricMap.reportMetrics()
		case <-agg.doneCh:
			t.Stop()
			return
		}
	}
}
<|endoftext|>


<commit_before>package goatee

import (
	"testing"
)

type testData struct {
	host    string
	subchan string
}

func setup(t *testing.T) *RedisClient {
	client := setupRedisConnection(t)
	setupConfig(t)

	return client
}

func setupRedisConnection(t *testing.T) *RedisClient {
	data := testData{host: ":6379", subchan: "c1"}
	client, err := NewRedisClient(data.host, data.subchan)
	if err != nil {
		t.Errorf("Error creating Redis client: %s", err)
	}

	return client
}

func setupConfig(t *testing.T) {
	LoadConfig("fixture/")
}
<commit_msg>update goatee test<commit_after>package goatee

import (
	"testing"
)

type testData struct {
	host    string
	subchan []string
}

func setup(t *testing.T) *RedisClient {
	client := setupRedisConnection(t)
	setupConfig(t)

	return client
}

func setupRedisConnection(t *testing.T) *RedisClient {
	data := testData{host: ":6379", subchan: []string{"c1", "c2"}}
	client, err := NewRedisClient(data.host, data.subchan)
	if err != nil {
		t.Errorf("Error creating Redis client: %s", err)
	}

	return client
}

func setupConfig(t *testing.T) {
	LoadConfig("fixture/")
}
<|endoftext|>


<commit_before>// +build go1.14

package mutagen

import (
	"encoding/binary"
	"fmt"
	"io"
	"net"

	"github.com/pkg/errors"
)

const (
	// VersionMajor represents the current major version of Mutagen.
	VersionMajor = 0
	// VersionMinor represents the current minor version of Mutagen.
	VersionMinor = 12
	// VersionPatch represents the current patch version of Mutagen.
	VersionPatch = 0
	// VersionTag represents a tag to be appended to the Mutagen version string.
	// It must not contain spaces. If empty, no tag is appended to the version
	// string.
	VersionTag = "alpha2"
)

// Version provides a stringified version of the current Mutagen version.
var Version string

// init performs global initialization.
func init() {
	// Compute the stringified version.
	if VersionTag != "" {
		Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag)
	} else {
		Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
	}
}

// versionBytes is a type that can be used to send and receive version
// information over the wire.
type versionBytes [12]byte

// sendVersion writes the current version to the specified writer. Version tag
// components are neither transmitted nor received.
func sendVersion(writer io.Writer) error {
	// Compute the version bytes.
	var data versionBytes
	binary.BigEndian.PutUint32(data[:4], VersionMajor)
	binary.BigEndian.PutUint32(data[4:8], VersionMinor)
	binary.BigEndian.PutUint32(data[8:], VersionPatch)

	// Transmit the bytes.
	_, err := writer.Write(data[:])
	return err
}

// receiveVersion reads version information from the specified reader. Version
// tag components are neither transmitted nor received.
func receiveVersion(reader io.Reader) (uint32, uint32, uint32, error) {
	// Read the bytes.
	var data versionBytes
	if _, err := io.ReadFull(reader, data[:]); err != nil {
		return 0, 0, 0, err
	}

	// Decode components.
	major := binary.BigEndian.Uint32(data[:4])
	minor := binary.BigEndian.Uint32(data[4:8])
	patch := binary.BigEndian.Uint32(data[8:])

	// Done.
	return major, minor, patch, nil
}

// ClientVersionHandshake performs the client side of a version handshake,
// returning an error if the received server version is not compatible with the
// client version.
//
// TODO: Add some ability to support version skew in this function.
func ClientVersionHandshake(connection net.Conn) error {
	// Receive the server's version.
	serverMajor, serverMinor, _, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive server version")
	}

	// Send our version to the server.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send client version")
	}

	// Ensure that our Mutagen versions are compatible. For now, we enforce that
	// they're equal at the minor release level.
	// TODO: Once we lock-in an internal protocol that we're going to support
	// for some time, we can allow some version skew. On the client side in
	// particular, we'll probably want to look out for the specific "locked-in"
	// server protocol that we support and instantiate some frozen client
	// implementation from that version.
	versionMatch := serverMajor == VersionMajor && serverMinor == VersionMinor
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}

// ServerVersionHandshake performs the server side of a version handshake,
// returning an error if the received client version is not compatible with the
// server version.
//
// TODO: Add some ability to support version skew in this function.
func ServerVersionHandshake(connection net.Conn) error {
	// Send our version to the client.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send server version")
	}

	// Receive the client's version.
	clientMajor, clientMinor, _, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive client version")
	}

	// Ensure that our versions are compatible. For now, we enforce that they're
	// equal at the minor release level.
	versionMatch := clientMajor == VersionMajor && clientMinor == VersionMinor
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}
<commit_msg>Bumped version to v0.12.0-alpha3.<commit_after>// +build go1.14

package mutagen

import (
	"encoding/binary"
	"fmt"
	"io"
	"net"

	"github.com/pkg/errors"
)

const (
	// VersionMajor represents the current major version of Mutagen.
	VersionMajor = 0
	// VersionMinor represents the current minor version of Mutagen.
	VersionMinor = 12
	// VersionPatch represents the current patch version of Mutagen.
	VersionPatch = 0
	// VersionTag represents a tag to be appended to the Mutagen version string.
	// It must not contain spaces. If empty, no tag is appended to the version
	// string.
	VersionTag = "alpha3"
)

// Version provides a stringified version of the current Mutagen version.
var Version string

// init performs global initialization.
func init() {
	// Compute the stringified version.
	if VersionTag != "" {
		Version = fmt.Sprintf("%d.%d.%d-%s", VersionMajor, VersionMinor, VersionPatch, VersionTag)
	} else {
		Version = fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
	}
}

// versionBytes is a type that can be used to send and receive version
// information over the wire.
type versionBytes [12]byte

// sendVersion writes the current version to the specified writer. Version tag
// components are neither transmitted nor received.
func sendVersion(writer io.Writer) error {
	// Compute the version bytes.
	var data versionBytes
	binary.BigEndian.PutUint32(data[:4], VersionMajor)
	binary.BigEndian.PutUint32(data[4:8], VersionMinor)
	binary.BigEndian.PutUint32(data[8:], VersionPatch)

	// Transmit the bytes.
	_, err := writer.Write(data[:])
	return err
}

// receiveVersion reads version information from the specified reader. Version
// tag components are neither transmitted nor received.
func receiveVersion(reader io.Reader) (uint32, uint32, uint32, error) {
	// Read the bytes.
	var data versionBytes
	if _, err := io.ReadFull(reader, data[:]); err != nil {
		return 0, 0, 0, err
	}

	// Decode components.
	major := binary.BigEndian.Uint32(data[:4])
	minor := binary.BigEndian.Uint32(data[4:8])
	patch := binary.BigEndian.Uint32(data[8:])

	// Done.
	return major, minor, patch, nil
}

// ClientVersionHandshake performs the client side of a version handshake,
// returning an error if the received server version is not compatible with the
// client version.
//
// TODO: Add some ability to support version skew in this function.
func ClientVersionHandshake(connection net.Conn) error {
	// Receive the server's version.
	serverMajor, serverMinor, _, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive server version")
	}

	// Send our version to the server.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send client version")
	}

	// Ensure that our Mutagen versions are compatible. For now, we enforce that
	// they're equal at the minor release level.
	// TODO: Once we lock-in an internal protocol that we're going to support
	// for some time, we can allow some version skew. On the client side in
	// particular, we'll probably want to look out for the specific "locked-in"
	// server protocol that we support and instantiate some frozen client
	// implementation from that version.
	versionMatch := serverMajor == VersionMajor && serverMinor == VersionMinor
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}

// ServerVersionHandshake performs the server side of a version handshake,
// returning an error if the received client version is not compatible with the
// server version.
//
// TODO: Add some ability to support version skew in this function.
func ServerVersionHandshake(connection net.Conn) error {
	// Send our version to the client.
	if err := sendVersion(connection); err != nil {
		return errors.Wrap(err, "unable to send server version")
	}

	// Receive the client's version.
	clientMajor, clientMinor, _, err := receiveVersion(connection)
	if err != nil {
		return errors.Wrap(err, "unable to receive client version")
	}

	// Ensure that our versions are compatible. For now, we enforce that they're
	// equal at the minor release level.
	versionMatch := clientMajor == VersionMajor && clientMinor == VersionMinor
	if !versionMatch {
		return errors.New("version mismatch")
	}

	// Success.
	return nil
}
<|endoftext|>


<commit_before>package godash

import (
	"reflect"
	"sort"
	"testing"
)

func TestIsPointer(t *testing.T) {
	x := &[]string{}
	y := []string{}

	if r := IsPointer(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsFunction(t *testing.T) {
	x := func() {}
	y := []string{}

	if r := IsFunction(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsSlice(t *testing.T) {
	x := []string{}
	y := func() {}

	if r := IsSlice(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsSlice(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsMap(t *testing.T) {
	x := make(map[string]interface{})
	y := 4
	z := make([]string, 0)

	if r := IsMap(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsMap(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r := IsMap(z); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestUnique(t *testing.T) {
	in := []string{"a", "a", "c", "d", "c"}
	out := []string{}
	b1 := []int{}

	// Test argument types
	if err := Unique(in, b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := Unique(in, &b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if err := Unique(in, &out); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if len(out) != 3 {
		t.Errorf("expected output slice length of 3 (got %v)", len(out))
	}
}

func TestSliceEqual(t *testing.T) {
	s1 := []string{"a", "b", "c"}
	s2 := []string{"c", "b", "a"}
	s3 := []string{"a", "b", "d", "c"}
	s4 := []int{1, 2, 3}
	x := 0

	// Test argument types
	if _, err := SliceEqual(s1, x); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := SliceEqual(s1, s4); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := SliceEqual(s1, x); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r, _ := SliceEqual(s1, s2); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s2); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s3); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s3); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s4); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIncludes(t *testing.T) {
	s1 := []string{"a", "b", "c"}

	// Test argument types
	if _, err := Includes(s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := Includes(1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := Includes(s1, "a"); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r, _ := Includes(s1, "d"); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestAppendUniq(t *testing.T) {
	// Test argument types
	x := 1
	s1 := []string{"a", "b", "c"}

	if err := AppendUniq(&x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, "d", 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	s2 := []string{"a", "b", "c"}
	expected := []string{"a", "b", "c", "d"}

	if err := AppendUniq(&s2, "a", "d", "a", "d"); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}

	sort.Strings(s2)
	sort.Strings(expected)

	if r := reflect.DeepEqual(s2, expected); !r {
		t.Errorf("expected correct slice (got %v)", s2)
	}
}

func TestKeys(t *testing.T) {
	m := map[string]interface{}{"a": 3, "b": false}
	a := []int{}
	b := 3
	c := []string{}

	// Test argument types
	if err := MapKeys(&m, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(a, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, a); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, &a); err == nil {
		// Test the output variable is a pointer of the valid type
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, &b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []string{"a", "b"}

	if err := MapKeys(m, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}

	sort.Strings(expected)
	sort.Strings(c)

	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}

func TestMapValues(t *testing.T) {
	m := map[string]int{"foo": 3, "bar": 6}
	a := []string{}
	b := 3
	c := []int{}

	// Test argument types
	if err := MapValues(&m, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(a, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, a); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, &a); err == nil {
		// Test the output variable is a pointer of the valid type
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, &b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []int{3, 6}

	if err := MapValues(m, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}

	sort.Ints(expected)
	sort.Ints(c)

	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}
<commit_msg>Implement unit tests for Intersect<commit_after>package godash

import (
	"reflect"
	"sort"
	"testing"
)

func TestIsPointer(t *testing.T) {
	x := &[]string{}
	y := []string{}

	if r := IsPointer(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsFunction(t *testing.T) {
	x := func() {}
	y := []string{}

	if r := IsFunction(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsPointer(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsSlice(t *testing.T) {
	x := []string{}
	y := func() {}

	if r := IsSlice(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsSlice(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIsMap(t *testing.T) {
	x := make(map[string]interface{})
	y := 4
	z := make([]string, 0)

	if r := IsMap(x); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r := IsMap(y); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r := IsMap(z); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestUnique(t *testing.T) {
	in := []string{"a", "a", "c", "d", "c"}
	out := []string{}
	b1 := []int{}

	// Test argument types
	if err := Unique(in, b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := Unique(in, &b1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if err := Unique(in, &out); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if len(out) != 3 {
		t.Errorf("expected output slice length of 3 (got %v)", len(out))
	}
}

func TestSliceEqual(t *testing.T) {
	s1 := []string{"a", "b", "c"}
	s2 := []string{"c", "b", "a"}
	s3 := []string{"a", "b", "d", "c"}
	s4 := []int{1, 2, 3}
	x := 0

	// Test argument types
	if _, err := SliceEqual(s1, x); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := SliceEqual(s1, s4); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := SliceEqual(s1, x); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if r, _ := SliceEqual(s1, s2); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s2); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s3); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
	if _, err := SliceEqual(s1, s3); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if r, _ := SliceEqual(s1, s4); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestIncludes(t *testing.T) {
	s1 := []string{"a", "b", "c"}

	// Test argument types
	if _, err := Includes(s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if _, err := Includes(1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	if r, _ := Includes(s1, "a"); !r {
		t.Errorf("expected 'true' (got %v)", r)
	}
	if r, _ := Includes(s1, "d"); r {
		t.Errorf("expected 'false' (got %v)", r)
	}
}

func TestAppendUniq(t *testing.T) {
	// Test argument types
	x := 1
	s1 := []string{"a", "b", "c"}

	if err := AppendUniq(&x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(x, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := AppendUniq(&s1, "d", 1); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	s2 := []string{"a", "b", "c"}
	expected := []string{"a", "b", "c", "d"}

	if err := AppendUniq(&s2, "a", "d", "a", "d"); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}

	sort.Strings(s2)
	sort.Strings(expected)

	if r := reflect.DeepEqual(s2, expected); !r {
		t.Errorf("expected correct slice (got %v)", s2)
	}
}

func TestKeys(t *testing.T) {
	m := map[string]interface{}{"a": 3, "b": false}
	a := []int{}
	b := 3
	c := []string{}

	// Test argument types
	if err := MapKeys(&m, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(a, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, a); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, &a); err == nil {
		// Test the output variable is a pointer of the valid type
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapKeys(m, &b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []string{"a", "b"}

	if err := MapKeys(m, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}

	sort.Strings(expected)
	sort.Strings(c)

	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}

func TestMapValues(t *testing.T) {
	m := map[string]int{"foo": 3, "bar": 6}
	a := []string{}
	b := 3
	c := []int{}

	// Test argument types
	if err := MapValues(&m, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(a, a); err == nil {
		// Test the input variable is a map
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, a); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, &a); err == nil {
		// Test the output variable is a pointer of the valid type
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}
	if err := MapValues(m, &b); err == nil {
		// Test the output variable is a pointer
		t.Errorf("expected an error (got %v)", err)
	}

	// Test correct functionality
	expected := []int{3, 6}

	if err := MapValues(m, &c); err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}

	sort.Ints(expected)
	sort.Ints(c)

	if ok := reflect.DeepEqual(c, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, c)
	}
}

func TestIntersect(t *testing.T) {
	s1 := []int{1, 2, 3, 4}
	s2 := []int{3, 4, 5, 1}
	s3 := []int{7, 8, 9, 0}
	s4 := []int{3, 4, 4, 3}
	x := 3
	y := []string{}
	out := []int{}

	// Validity tests

	// Test if the first slice is actually a slice
	if err := Intersect(x, s2, &out); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test if the second slice is actually a slice
	if err := Intersect(s1, x, &out); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test if the output argument is a pointer
	if err := Intersect(x, s2, out); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test if the output argument is a pointer to a slice
	if err := Intersect(s1, s2, &x); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test if the two slices are of the same type
	if err := Intersect(s1, y, &out); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}
	if err := Intersect(y, s2, &out); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Test if the output slice reference is of the same type
	if err := Intersect(s1, s2, &y); err == nil {
		t.Errorf("expected an error (got %v)", err)
	}

	// Functionality tests
	var (
		expected []int
		err      error
	)

	// Test when there is an intersection
	out = []int{}
	expected = []int{3, 4, 1}
	err = Intersect(s1, s2, &out)

	sort.Ints(expected)
	sort.Ints(out)

	if err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if ok := reflect.DeepEqual(out, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, out)
	}

	// Test when there is no intersection
	out = []int{}
	expected = []int{}
	err = Intersect(s1, s3, &out)

	if err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if ok := reflect.DeepEqual(out, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, out)
	}

	// Test intersection with duplicates
	out = []int{}
	expected = []int{3, 4}
	err = Intersect(s1, s4, &out)

	sort.Ints(expected)
	sort.Ints(out)

	if err != nil {
		t.Errorf("expected nil error (got %v)", err)
	}
	if ok := reflect.DeepEqual(out, expected); !ok {
		t.Errorf("expected (%v) (got %v)", expected, out)
	}
}
<|endoftext|>


<commit_before>package golem

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"

	docker "github.com/fsouza/go-dockerclient"
)

func getPort() int {
	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		panic(err)
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port
}

func getName(id string) string {
	return fmt.Sprintf("golem-%s", id)
}

func containersMatching(client *docker.Client, matches func(container *docker.APIContainers) bool) ([]docker.APIContainers, error) {
	containers, err := client.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		log.WithError(err).Error("Error listing containers")
		return nil, err
	}

	matching := []docker.APIContainers{}
	for _, container := range containers {
		if matches(&container) {
			matching = append(matching, container)
		}
	}
	return matching, nil
}

func containerHasName(container *docker.APIContainers, name string) bool {
	for _, containerName := range container.Names {
		if containerName == "/"+name {
			return true
		}
	}
	return false
}

// Spawn will create a new container and inject a golem into it
func Spawn(webstrateID string) (string, error) {

	client, err := docker.NewClientFromEnv()
	if err != nil {
		log.WithError(err).Error("Could not create docker client")
		return "", err
	}

	repository := "webstrates/golem"
	tag := "latest"

	log.WithFields(log.Fields{"image": fmt.Sprintf("%s:%s", repository, tag)}).Info("Pulling image")

	err = client.PullImage(docker.PullImageOptions{
		Repository: "webstrates/golem",
		Tag:        "latest",
	}, docker.AuthConfiguration{})
	if err != nil {
		return "", err
	}
	log.WithFields(log.Fields{"image": fmt.Sprintf("%s:%s", repository, tag)}).Info("Pull done")

	// Get current dir
	dir, err := os.Getwd()
	if err != nil {
		log.WithError(err).Error("Could not discover current directory")
		return "", err
	}

	seccomp, err := ioutil.ReadFile(filepath.Join(dir, "chrome.json"))
	if err != nil {
		log.WithError(err).Error("Could not read seccomp profile")
		return "", err
	}

	log.WithFields(log.Fields{"webstrateid": webstrateID}).Info("Creating container")
	container, err := client.CreateContainer(
		docker.CreateContainerOptions{
			Name: getName(webstrateID),
			Config: &docker.Config{
				Image: "webstrates/golem:latest",
				ExposedPorts: map[docker.Port]struct{}{
					"9222/tcp": {},
				},
				Env: []string{fmt.Sprintf("WEBSTRATEID=%s", webstrateID)},
				Cmd: []string{
					"--headless",
					"--ignore-certificate-errors",
					"--disable-gpu",
					"--remote-debugging-address=0.0.0.0",
					"--remote-debugging-port=9222",
					fmt.Sprintf("http://webstrates/%s", webstrateID),
				},
			},
			HostConfig: &docker.HostConfig{
				Links: []string{"webstrates"},
				PortBindings: map[docker.Port][]docker.PortBinding{
					"9222/tcp": []docker.PortBinding{
						docker.PortBinding{
							HostIP:   "0.0.0.0",
							HostPort: fmt.Sprintf("%d", getPort()),
						},
					},
				},
				SecurityOpt: []string{
					fmt.Sprintf("seccomp=%s", string(seccomp)),
				},
			},
		},
	)
	if err != nil {
		log.WithError(err).Error("Error creating container")
		return "", err
	}
	log.WithFields(log.Fields{"webstrateid": webstrateID, "containerid": container.ID}).Info("Created container, starting ...")

	err = client.StartContainer(container.ID, nil)

	if err != nil {
		log.WithError(err).Error("Error starting container")
		return "", err
	}
	return container.ID, nil
}

// Kill will kill the container running the given golem
func Kill(webstrateID string) error {

	client, err := docker.NewClientFromEnv()
	if err != nil {
		log.WithError(err).Error("Error creating docker client")
		return err
	}

	golems, err := containersMatching(client, func(c *docker.APIContainers) bool {
		return strings.HasPrefix(c.Image, "webstrates/golem") && containerHasName(c, getName(webstrateID))
	})

	if len(golems) != 1 {
		return fmt.Errorf("Unexpected amount of golems - %d", len(golems))
	}

	err = client.KillContainer(docker.KillContainerOptions{
		ID: golems[0].ID,
	})
	if err != nil {
		return err
	}

	err = client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            golems[0].ID,
		RemoveVolumes: true,
	})
	if err != nil {
		return err
	}
	return nil
}

// Restart will kill, recreate and start a given golem
func Restart(webstrateID string) (string, error) {
	err := Kill(webstrateID)
	if err != nil {
		return "", err
	}
	return Spawn(webstrateID)
}

// List the running golems
func List() ([]docker.APIContainers, error) {

	client, err := docker.NewClientFromEnv()
	if err != nil {
		log.WithError(err).Error("Error creating docker client")
		return nil, err
	}

	golems, err := containersMatching(client, func(container *docker.APIContainers) bool {
		return strings.HasPrefix(container.Image, "webstrates/golem")
	})
	if err != nil {
		return nil, err
	}

	return golems, nil

}
<commit_msg>Finding a port<commit_after>package golem

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"path/filepath"
	"strings"

	log "github.com/Sirupsen/logrus"

	docker "github.com/fsouza/go-dockerclient"
)

func getPort() int {
	addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
	if err != nil {
		panic(err)
	}

	l, err := net.ListenTCP("tcp", addr)
	if err != nil {
		panic(err)
	}
	defer l.Close()
	return l.Addr().(*net.TCPAddr).Port
}

func getName(id string) string {
	return fmt.Sprintf("golem-%s", id)
}

func containersMatching(client *docker.Client, matches func(container *docker.APIContainers) bool) ([]docker.APIContainers, error) {
	containers, err := client.ListContainers(docker.ListContainersOptions{All: false})
	if err != nil {
		log.WithError(err).Error("Error listing containers")
		return nil, err
	}

	matching := []docker.APIContainers{}
	for _, container := range containers {
		if matches(&container) {
			matching = append(matching, container)
		}
	}
	return matching, nil
}

func containerHasName(container *docker.APIContainers, name string) bool {
	for _, containerName := range container.Names {
		if containerName == "/"+name {
			return true
		}
	}
	return false
}

// Spawn will create a new container and inject a golem into it
func Spawn(webstrateID string) (string, error) {

	client, err := docker.NewClientFromEnv()
	if err != nil {
		log.WithError(err).Error("Could not create docker client")
		return "", err
	}

	repository := "webstrates/golem"
	tag := "latest"

	log.WithFields(log.Fields{"image": fmt.Sprintf("%s:%s", repository, tag)}).Info("Pulling image")

	err = client.PullImage(docker.PullImageOptions{
		Repository: "webstrates/golem",
		Tag:        "latest",
	}, docker.AuthConfiguration{})
	if err != nil {
		return "", err
	}
	log.WithFields(log.Fields{"image": fmt.Sprintf("%s:%s", repository, tag)}).Info("Pull done")

	// Get current dir
	dir, err := os.Getwd()
	if err != nil {
		log.WithError(err).Error("Could not discover current directory")
		return "", err
	}

	seccomp, err := ioutil.ReadFile(filepath.Join(dir, "chrome.json"))
	if err != nil {
		log.WithError(err).Error("Could not read seccomp profile")
		return "", err
	}

	log.WithFields(log.Fields{"webstrateid": webstrateID}).Info("Creating container")
	container, err := client.CreateContainer(
		docker.CreateContainerOptions{
			Name: getName(webstrateID),
			Config: &docker.Config{
				Image: "webstrates/golem:latest",
				ExposedPorts: map[docker.Port]struct{}{
					"9222/tcp": {},
				},
				Env: []string{fmt.Sprintf("WEBSTRATEID=%s", webstrateID)},
				Cmd: []string{
					"--headless",
					"--ignore-certificate-errors",
					"--disable-gpu",
					"--remote-debugging-address=0.0.0.0",
					"--remote-debugging-port=9222",
					fmt.Sprintf("http://webstrates/%s", webstrateID),
				},
			},
			HostConfig: &docker.HostConfig{
				Links: []string{"webstrates"},
				PortBindings: map[docker.Port][]docker.PortBinding{
					"9222/tcp": []docker.PortBinding{
						docker.PortBinding{
							HostIP:   "0.0.0.0",
							HostPort: fmt.Sprintf("%d", getPort()),
						},
					},
				},
				SecurityOpt: []string{
					fmt.Sprintf("seccomp=%s", string(seccomp)),
				},
			},
		},
	)
	if err != nil {
		log.WithError(err).Error("Error creating container")
		return "", err
	}
	log.WithFields(log.Fields{"webstrateid": webstrateID, "containerid": container.ID}).Info("Created container, starting ...")

	err = client.StartContainer(container.ID, nil)

	if err != nil {
		log.WithError(err).Error("Error starting container")
		return "", err
	}
	return container.ID, nil
}

// Kill will kill the container running the given golem
func Kill(webstrateID string) error {

	client, err := docker.NewClientFromEnv()
	if err != nil {
		log.WithError(err).Error("Error creating docker client")
		return err
	}

	golems, err := containersMatching(client, func(c *docker.APIContainers) bool {
		return strings.HasPrefix(c.Image, "webstrates/golem") && containerHasName(c, getName(webstrateID))
	})

	if len(golems) != 1 {
		return fmt.Errorf("Unexpected amount of golems - %d", len(golems))
	}

	err = client.KillContainer(docker.KillContainerOptions{
		ID: golems[0].ID,
	})
	if err != nil {
		return err
	}

	err = client.RemoveContainer(docker.RemoveContainerOptions{
		ID:            golems[0].ID,
		RemoveVolumes: true,
	})
	if err != nil {
		return err
	}
	return nil
}

// Restart will kill, recreate and start a given golem
func Restart(webstrateID string) (string, error) {
	err := Kill(webstrateID)
	if err != nil {
		return "", err
	}
	return Spawn(webstrateID)
}

// PortOf returns the public port mapped to the given privatePort for
// a golem on the given webstrate.
func PortOf(webstrate string, privatePort int64) (int64, error) {
	// Not very effective to do a list each time the port is needed
	golems, err := List()
	if err != nil {
		return -1, err
	}

	for _, golem := range golems {
		if ws, ok := golem.Labels["webstrate"]; ok && webstrate == ws {
			for _, port := range golem.Ports {
				if port.PrivatePort == privatePort {
					return port.PublicPort, nil
				}
			}
		}
	}

	return -1, fmt.Errorf("No container found for webstrate")
}

// List the running golems
func List() ([]docker.APIContainers, error) {

	client, err := docker.NewClientFromEnv()
	if err != nil {
		log.WithError(err).Error("Error creating docker client")
		return nil, err
	}

	golems, err := containersMatching(client, func(container *docker.APIContainers) bool {
		return strings.HasPrefix(container.Image, "webstrates/golem")
	})
	if err != nil {
		return nil, err
	}

	return golems, nil

}
<|endoftext|>


<commit_before>package golisp

import (
	"log"
	"testing"
)

var testdata = []struct {
	expression []string
	result     []string
}{
	{[]string{"(+ 2 3)"}, []string{"5"}},
	{[]string{"(+ 1 6)"}, []string{"7"}},
	{[]string{"(oddp (+ 1 6))"}, []string{"t"}},
	{[]string{"(* 3 (+ 1 6))"}, []string{"21"}},
	{[]string{"(/ (* 2 11) (+ 1 6))"}, []string{"22/7"}},
	{[]string{"23"}, []string{"23"}},
	{[]string{"t"}, []string{"t"}},
	{[]string{"nil"}, []string{"nil"}},
	{[]string{"(equal (+ 7 5) (* 2 8))"}, []string{"nil"}},
	{[]string{"(/ (+ 6 8) 2.0)"}, []string{"7.0"}},
	{[]string{"(defun average (x y) (/ (+ x y) 2.0))", "(average 6 8)"}, []string{"nil", "7.0"}},
	{[]string{"(defun square (n) (* n n))", "(square 2)"}, []string{"nil", "4"}},
	{[]string{"(defun total-cost (quantity price handling-charge) (+ (* quantity price) handling-charge))", "(total-cost 2 3 4)"}, []string{"nil", "10"}},
	{[]string{"pi"}, []string{"3.14159"}},
	{[]string{"(equal 'kirk 'spock)"}, []string{"nil"}},
	{[]string{"(list 'james t 'kirk)"}, []string{"(james t kirk)"}},
	{[]string{"(defun riddle (x y) (list 'why 'is 'a x 'like 'a y))", "(riddle 'raven 'writing-desk)"}, []string{"nil", "(why is a raven like a writing-desk)"}},
	{[]string{"(first (list 1 2 3))"}, []string{"1"}},
	{[]string{"(first '(we hold these truths))"}, []string{"we"}},
	{[]string{"'(+ 1 2)"}, []string{"(+ 1 2)"}},
	{[]string{"(oddp (+ 1 2))"}, []string{"t"}},
	{[]string{"(list 'a 'b 'c)"}, []string{"(a b c)"}},
	{[]string{"(cons 'a '(b c))"}, []string{"(a b c)"}},
	{[]string{"(+ 10 (- 5 2))"}, []string{"13"}},
	{[]string{"(list 'buy '(* 27 34) 'bagels)"}, []string{"(buy (* 27 34) bagels)"}},
	{[]string{"(list 'buy (* 27 34) 'bagels)"}, []string{"(buy 918 bagels)"}},
	{[]string{"'(foo bar baz)"}, []string{"(foo bar baz)"}},
	{[]string{"(list 'foo 'bar 'baz)"}, []string{"(foo bar baz)"}},
	{[]string{"(cons 'foo '(bar baz))"}, []string{"(foo bar baz)"}},
	{[]string{"(list 33 'squared 'is (* 33 33))"}, []string{"(33 squared is 1089)"}},
	{[]string{"'(33 squared is (* 33 33))"}, []string{"(33 squared is (* 33 33))"}},
	{[]string{"(defun intro (x y) (list x 'this 'is y))", "(intro 'stanley 'livingstone)"}, []string{"nil", "(stanley this is livingstone)"}},
	{[]string{"(defun intro (x y) (list 'x 'this 'is 'y))", "(intro 'stanley 'livingstone)"}, []string{"nil", "(x this is y)"}},
	{[]string{"(defun double (n) (* n 2))", "(defun quadruple (n) (double (double n)))", "(quadruple 5)"}, []string{"nil", "nil", "20"}},
	{[]string{"(defun test () (* 85 97))", "(test)"}, []string{"nil", "8245"}},
	{[]string{"(quote foo)"}, []string{"foo"}},
	{[]string{"(quote (hello world))"}, []string{"(hello world)"}},
	{[]string{"'foo"}, []string{"foo"}},
	{[]string{"''foo"}, []string{"(quote foo)"}},
	{[]string{"(list 'quote 'foo)"}, []string{"(quote foo)"}},
	{[]string{"(first ''foo)"}, []string{"quote"}},
	{[]string{"(rest ''foo)"}, []string{"(foo)"}},
	{[]string{"(length ''foo)"}, []string{"2"}},
	{[]string{"'(+ 2 2)"}, []string{"(+ 2 2)"}},
	{[]string{"(eval '(+ 2 2))"}, []string{"4"}},
	{[]string{"'''boing"}, []string{"(quote (quote boing))"}},
	{[]string{"(eval '''boing)"}, []string{"(quote boing)"}},
	{[]string{"(eval (eval '''boing))"}, []string{"boing"}},
	{[]string{"'(list '* 9 6)"}, []string{"(list (quote *) 9 6)"}},
	{[]string{"(eval '(list '* 9 6))"}, []string{"(* 9 6)"}},
	{[]string{"(eval (eval '(list '* 9 6)))"}, []string{"54"}},
	{[]string{"(apply #'+ '(2 3))"}, []string{"5"}},
	{[]string{"(apply #'equal '(12 17))"}, []string{"nil"}},
	{[]string{"(apply #'cons '(as (you like it)))"}, []string{"(as you like it)"}},
	{[]string{"(if (oddp 1) 'odd 'even)"}, []string{"odd"}},
	{[]string{"(if (oddp 2) 'odd 'even)"}, []string{"even"}},
	{[]string{"(if t 'test-was-true 'test-was-false)"}, []string{"test-was-true"}},
	{[]string{"(if nil 'test-was-true 'test-was-false)"}, []string{"test-was-false"}},
	{[]string{"(if (symbolp 'foo) (* 5 5) (+ 5 5))"}, []string{"25"}},
	{[]string{"(if (symbolp 1) (* 5 5) (+ 5 5))"}, []string{"10"}},
	{[]string{"(defun my-abs (x) (if (< x 0) (- x) x))", "(my-abs -5)", "(my-abs 5)"}, []string{"nil", "5", "5"}},
	{[]string{"(defun symbol-test (x) (if (symbolp x) (list 'yes x 'is 'a 'symbol) (list 'no x 'is 'not 'a 'symbol)))", "(symbol-test 'rutabaga)", "(symbol-test 12345)"}, []string{"nil", "(yes rutabaga is a symbol)", "(no 12345 is not a symbol)"}},
	{[]string{"(if t 'happy)"}, []string{"happy"}},
	{[]string{"(if nil 'happy)"}, []string{"nil"}},
	{[]string{"(defun compare (x y) (cond ((equal x y) 'numbers-are-the-same) ((< x y) 'first-is-smaller) ((> x y) 'first-is-bigger)))", "(compare 3 5)", "(compare 7 2)", "(compare 4 4)"}, []string{"nil", "first-is-smaller", "first-is-bigger", "numbers-are-the-same"}},
	{[]string{"(defun compare (x y) (cond ((< x y) 'first-is-smaller) ((> x y) 'first-is-bigger)))", "(compare 5 5)"}, []string{"nil", "nil"}},
	{[]string{"(defun where-is (x) (cond ((equal x 'paris) 'france) ((equal x 'london) 'england) ((equal x 'beijing) 'china) (t 'unknown)))", "(where-is 'london)", "(where-is 'beijing)", "(where-is 'hackensack)"}, []string{"nil", "england", "china", "unknown"}},
	{[]string{"(defun emphasize (x) (cond ((equal (first x) 'good) (cons 'great (rest x))) ((equal (first x) 'bad) (cons 'awful (rest x)))))", "(emphasize '(good mystery story))", "(emphasize '(mediocre mystery story))"}, []string{"nil", "(great mystery story)", "nil"}},
	{[]string{"(defun emphasize2 (x) (cond ((equal (first x) 'good) (cons 'great (rest x))) ((equal (first x) 'bad) (cons 'awful (rest x))) (t x)))", "(emphasize2 '(good day))", "(emphasize2 '(bad day))", "(emphasize2 '(long day))"}, []string{"nil", "(great day)", "(awful day)", "(long day)"}},
	{[]string{"(defun compute (op x y) (cond ((equal op 'sum-of) (+ x y)) ((equal op 'product-of) (* x y)) (t '(that does not compute))))", "(compute 'sum-of 3 7)", "(compute 'product-of 2 4)", "(compute 'zorch-of 3 1)"}, []string{"nil", "10", "8", "(that does not compute)"}},
	{[]string{"(defun double (n) (* n 2))", "(double 5)"}, []string{"nil", "10"}},
	{[]string{"(setf vowels '(a e i o u))", "(length vowels)", "(rest vowels)", "vowels", "(setf vowels '(a e i o u and sometimes y))", "(rest (rest vowels))"}, []string{"(a e i o u)", "5", "(e i o u)", "(a e i o u)", "(a e i o u and sometimes y)", "(i o u and sometimes y)"}},
}

var baddata = []struct {
	expression []string
	fail       []bool
	message    []string
}{
	{[]string{"(1 2 3)"}, []bool{true}, []string{""}},
	{[]string{"(defun average (x y) (/ (+ x y) 2.0))", "(average 6 8 7)"}, []bool{false, true}, []string{"", ""}},
	{[]string{"(equal kirk spock)"}, []bool{true}, []string{""}},
	{[]string{"(list kirk 1 2)"}, []bool{true}, []string{""}},
	{[]string{"(first (we hold these truths))"}, []bool{true}, []string{"Error! 'we' undefined function"}},
	{[]string{"(first 1 2 3 4)"}, []bool{true}, []string{""}},
	{[]string{"(oddp '(+ 1 2))"}, []bool{true}, []string{"Error! Wrong type input to oddp"}},
	{[]string{"(cons 'a (b c))"}, []bool{true}, []string{"Error! 'b' undefined function"}},
	{[]string{"(+ 10 '(- 5 2))"}, []bool{true}, []string{"Error! Wrong type input to +"}},
	{[]string{"(- 10 '(- 5 2))"}, []bool{true}, []string{"Error! Wrong type input to -"}},
	{[]string{"('foo 'bar 'baz)"}, []bool{true}, []string{"Error! 'foo' undefined function"}},
	{[]string{"(list foo bar baz)"}, []bool{true}, []string{"Error! foo unassigned variable"}},
	{[]string{"(foo bar baz)"}, []bool{true}, []string{"Error! 'foo' undefined function"}},
	{[]string{"(defun intro ('x 'y) (list x 'this 'is y))"}, []bool{true}, []string{"Bad argument list"}},
	{[]string{"(defun intro ((x) (y)) (list x 'this 'is y))"}, []bool{true}, []string{"Bad argument list"}},
	{[]string{"(defun intro (x y) (list (x) 'this 'is (y)))", "(intro 'stanley 'livingstone)"}, []bool{false, true}, []string{"", "Error! 'x' undefined function"}},
	{[]string{"(defun intro (x y) (list x this is y))", "(intro 'stanley 'livingstone)"}, []bool{false, true}, []string{"", "Error! this unassigned variable"}},
	{[]string{"(defun double (n) (* n 2))", "n"}, []bool{false, true}, []string{"", "Error! n unassigned variable"}},
	{[]string{"(defun test () (* 85 97))", "(test 1)"}, []bool{false, true}, []string{"", "Error! Too many arguments"}},
	{[]string{"(defun test () (* 85 97))", "test"}, []bool{false, true}, []string{"", "Error! test unassigned variable"}},
	{[]string{"(eval (eval (eval '''boing)))"}, []bool{true}, []string{"Error! boing unassigned variable"}},
	{[]string{"(defun double (n) (* n 2))", "(double 5)", "n"}, []bool{false, false, true}, []string{"nil", "10", "Error! n unassigned variable"}},
}

func TestGolispBad(t *testing.T) {
	for _, test := range baddata {
		i := Init()
		for j := range test.expression {
			log.Printf("TESTING %v", test.expression[j])
			e := Parse(test.expression[j])
			p, err := i.Eval(e.(Primitive), make([]Variable, 0))
			log.Printf("TESTING %v with %v", p, err)
			if test.fail[j] && err == nil {
				t.Errorf("Executing %v has not failed and it should have done: %v -> %v", e.str(), p, p.str())
			} else if !test.fail[j] && err != nil {
				t.Errorf("Executing %v has failed and it shouldn't have done: %v -> %v leads to %v", e.str(), p, p, err)
			}
			if err != nil && test.message[j] != "" {
				if test.message[j] != err.Error() {
					t.Errorf("Error messages don't match %v vs %v", test.message[j], err.Error())
				}
			}
		}
	}
}

func TestGolispGood(t *testing.T) {
	for _, test := range testdata {
		i := Init()
		for j := range test.expression {
			log.Printf("Running test on %v", test.expression[j])
			e := Parse(test.expression[j])
			r, err := i.Eval(e.(Primitive), make([]Variable, 0))
			if err != nil {
				t.Fatalf("Executing %v has failed for %v", e.str(), err)
			} else if r.str() != test.result[j] {
				t.Fatalf("%v did not lead to %v, it lead to %v with %v", test.expression[j], test.result[j], r.str(), test.expression)
			}
		}
	}
}
<commit_msg>Added test of number 73. This closes #131<commit_after>package golisp

import (
	"log"
	"testing"
)

var testdata = []struct {
	expression []string
	result     []string
}{
	{[]string{"(+ 2 3)"}, []string{"5"}},
	{[]string{"(+ 1 6)"}, []string{"7"}},
	{[]string{"(oddp (+ 1 6))"}, []string{"t"}},
	{[]string{"(* 3 (+ 1 6))"}, []string{"21"}},
	{[]string{"(/ (* 2 11) (+ 1 6))"}, []string{"22/7"}},
	{[]string{"23"}, []string{"23"}},
	{[]string{"t"}, []string{"t"}},
	{[]string{"nil"}, []string{"nil"}},
	{[]string{"(equal (+ 7 5) (* 2 8))"}, []string{"nil"}},
	{[]string{"(/ (+ 6 8) 2.0)"}, []string{"7.0"}},
	{[]string{"(defun average (x y) (/ (+ x y) 2.0))", "(average 6 8)"}, []string{"nil", "7.0"}},
	{[]string{"(defun square (n) (* n n))", "(square 2)"}, []string{"nil", "4"}},
	{[]string{"(defun total-cost (quantity price handling-charge) (+ (* quantity price) handling-charge))", "(total-cost 2 3 4)"}, []string{"nil", "10"}},
	{[]string{"pi"}, []string{"3.14159"}},
	{[]string{"(equal 'kirk 'spock)"}, []string{"nil"}},
	{[]string{"(list 'james t 'kirk)"}, []string{"(james t kirk)"}},
	{[]string{"(defun riddle (x y) (list 'why 'is 'a x 'like 'a y))", "(riddle 'raven 'writing-desk)"}, []string{"nil", "(why is a raven like a writing-desk)"}},
	{[]string{"(first (list 1 2 3))"}, []string{"1"}},
	{[]string{"(first '(we hold these truths))"}, []string{"we"}},
	{[]string{"'(+ 1 2)"}, []string{"(+ 1 2)"}},
	{[]string{"(oddp (+ 1 2))"}, []string{"t"}},
	{[]string{"(list 'a 'b 'c)"}, []string{"(a b c)"}},
	{[]string{"(cons 'a '(b c))"}, []string{"(a b c)"}},
	{[]string{"(+ 10 (- 5 2))"}, []string{"13"}},
	{[]string{"(list 'buy '(* 27 34) 'bagels)"}, []string{"(buy (* 27 34) bagels)"}},
	{[]string{"(list 'buy (* 27 34) 'bagels)"}, []string{"(buy 918 bagels)"}},
	{[]string{"'(foo bar baz)"}, []string{"(foo bar baz)"}},
	{[]string{"(list 'foo 'bar 'baz)"}, []string{"(foo bar baz)"}},
	{[]string{"(cons 'foo '(bar baz))"}, []string{"(foo bar baz)"}},
	{[]string{"(list 33 'squared 'is (* 33 33))"}, []string{"(33 squared is 1089)"}},
	{[]string{"'(33 squared is (* 33 33))"}, []string{"(33 squared is (* 33 33))"}},
	{[]string{"(defun intro (x y) (list x 'this 'is y))", "(intro 'stanley 'livingstone)"}, []string{"nil", "(stanley this is livingstone)"}},
	{[]string{"(defun intro (x y) (list 'x 'this 'is 'y))", "(intro 'stanley 'livingstone)"}, []string{"nil", "(x this is y)"}},
	{[]string{"(defun double (n) (* n 2))", "(defun quadruple (n) (double (double n)))", "(quadruple 5)"}, []string{"nil", "nil", "20"}},
	{[]string{"(defun test () (* 85 97))", "(test)"}, []string{"nil", "8245"}},
	{[]string{"(quote foo)"}, []string{"foo"}},
	{[]string{"(quote (hello world))"}, []string{"(hello world)"}},
	{[]string{"'foo"}, []string{"foo"}},
	{[]string{"''foo"}, []string{"(quote foo)"}},
	{[]string{"(list 'quote 'foo)"}, []string{"(quote foo)"}},
	{[]string{"(first ''foo)"}, []string{"quote"}},
	{[]string{"(rest ''foo)"}, []string{"(foo)"}},
	{[]string{"(length ''foo)"}, []string{"2"}},
	{[]string{"'(+ 2 2)"}, []string{"(+ 2 2)"}},
	{[]string{"(eval '(+ 2 2))"}, []string{"4"}},
	{[]string{"'''boing"}, []string{"(quote (quote boing))"}},
	{[]string{"(eval '''boing)"}, []string{"(quote boing)"}},
	{[]string{"(eval (eval '''boing))"}, []string{"boing"}},
	{[]string{"'(list '* 9 6)"}, []string{"(list (quote *) 9 6)"}},
	{[]string{"(eval '(list '* 9 6))"}, []string{"(* 9 6)"}},
	{[]string{"(eval (eval '(list '* 9 6)))"}, []string{"54"}},
	{[]string{"(apply #'+ '(2 3))"}, []string{"5"}},
	{[]string{"(apply #'equal '(12 17))"}, []string{"nil"}},
	{[]string{"(apply #'cons '(as (you like it)))"}, []string{"(as you like it)"}},
	{[]string{"(if (oddp 1) 'odd 'even)"}, []string{"odd"}},
	{[]string{"(if (oddp 2) 'odd 'even)"}, []string{"even"}},
	{[]string{"(if t 'test-was-true 'test-was-false)"}, []string{"test-was-true"}},
	{[]string{"(if nil 'test-was-true 'test-was-false)"}, []string{"test-was-false"}},
	{[]string{"(if (symbolp 'foo) (* 5 5) (+ 5 5))"}, []string{"25"}},
	{[]string{"(if (symbolp 1) (* 5 5) (+ 5 5))"}, []string{"10"}},
	{[]string{"(defun my-abs (x) (if (< x 0) (- x) x))", "(my-abs -5)", "(my-abs 5)"}, []string{"nil", "5", "5"}},
	{[]string{"(defun symbol-test (x) (if (symbolp x) (list 'yes x 'is 'a 'symbol) (list 'no x 'is 'not 'a 'symbol)))", "(symbol-test 'rutabaga)", "(symbol-test 12345)"}, []string{"nil", "(yes rutabaga is a symbol)", "(no 12345 is not a symbol)"}},
	{[]string{"(if t 'happy)"}, []string{"happy"}},
	{[]string{"(if nil 'happy)"}, []string{"nil"}},
	{[]string{"(defun compare (x y) (cond ((equal x y) 'numbers-are-the-same) ((< x y) 'first-is-smaller) ((> x y) 'first-is-bigger)))", "(compare 3 5)", "(compare 7 2)", "(compare 4 4)"}, []string{"nil", "first-is-smaller", "first-is-bigger", "numbers-are-the-same"}},
	{[]string{"(defun compare (x y) (cond ((< x y) 'first-is-smaller) ((> x y) 'first-is-bigger)))", "(compare 5 5)"}, []string{"nil", "nil"}},
	{[]string{"(defun where-is (x) (cond ((equal x 'paris) 'france) ((equal x 'london) 'england) ((equal x 'beijing) 'china) (t 'unknown)))", "(where-is 'london)", "(where-is 'beijing)", "(where-is 'hackensack)"}, []string{"nil", "england", "china", "unknown"}},
	{[]string{"(defun emphasize (x) (cond ((equal (first x) 'good) (cons 'great (rest x))) ((equal (first x) 'bad) (cons 'awful (rest x)))))", "(emphasize '(good mystery story))", "(emphasize '(mediocre mystery story))"}, []string{"nil", "(great mystery story)", "nil"}},
	{[]string{"(defun emphasize2 (x) (cond ((equal (first x) 'good) (cons 'great (rest x))) ((equal (first x) 'bad) (cons 'awful (rest x))) (t x)))", "(emphasize2 '(good day))", "(emphasize2 '(bad day))", "(emphasize2 '(long day))"}, []string{"nil", "(great day)", "(awful day)", "(long day)"}},
	{[]string{"(defun compute (op x y) (cond ((equal op 'sum-of) (+ x y)) ((equal op 'product-of) (* x y)) (t '(that does not compute))))", "(compute 'sum-of 3 7)", "(compute 'product-of 2 4)", "(compute 'zorch-of 3 1)"}, []string{"nil", "10", "8", "(that does not compute)"}},
	{[]string{"(defun double (n) (* n 2))", "(double 5)"}, []string{"nil", "10"}},
	{[]string{"(setf vowels '(a e i o u))", "(length vowels)", "(rest vowels)", "vowels", "(setf vowels '(a e i o u and sometimes y))", "(rest (rest vowels))"}, []string{"(a e i o u)", "5", "(e i o u)", "(a e i o u)", "(a e i o u and sometimes y)", "(i o u and sometimes y)"}},
	{[]string{"(setf long-list '(a b c d e f g h i))", "(setf head (first long-list))", "(setf tail (rest long-list))", "(cons head tail)", "(equal long-list (cons head tail))", "(list head tail)"}, []string{"(a b c d e f g h i)", "a", "(b c d e f g h i)", "(a b c d e f g h i)", "t", "(a (b c d e f g h i))"}},
}

var baddata = []struct {
	expression []string
	fail       []bool
	message    []string
}{
	{[]string{"(1 2 3)"}, []bool{true}, []string{""}},
	{[]string{"(defun average (x y) (/ (+ x y) 2.0))", "(average 6 8 7)"}, []bool{false, true}, []string{"", ""}},
	{[]string{"(equal kirk spock)"}, []bool{true}, []string{""}},
	{[]string{"(list kirk 1 2)"}, []bool{true}, []string{""}},
	{[]string{"(first (we hold these truths))"}, []bool{true}, []string{"Error! 'we' undefined function"}},
	{[]string{"(first 1 2 3 4)"}, []bool{true}, []string{""}},
	{[]string{"(oddp '(+ 1 2))"}, []bool{true}, []string{"Error! Wrong type input to oddp"}},
	{[]string{"(cons 'a (b c))"}, []bool{true}, []string{"Error! 'b' undefined function"}},
	{[]string{"(+ 10 '(- 5 2))"}, []bool{true}, []string{"Error! Wrong type input to +"}},
	{[]string{"(- 10 '(- 5 2))"}, []bool{true}, []string{"Error! Wrong type input to -"}},
	{[]string{"('foo 'bar 'baz)"}, []bool{true}, []string{"Error! 'foo' undefined function"}},
	{[]string{"(list foo bar baz)"}, []bool{true}, []string{"Error! foo unassigned variable"}},
	{[]string{"(foo bar baz)"}, []bool{true}, []string{"Error! 'foo' undefined function"}},
	{[]string{"(defun intro ('x 'y) (list x 'this 'is y))"}, []bool{true}, []string{"Bad argument list"}},
	{[]string{"(defun intro ((x) (y)) (list x 'this 'is y))"}, []bool{true}, []string{"Bad argument list"}},
	{[]string{"(defun intro (x y) (list (x) 'this 'is (y)))", "(intro 'stanley 'livingstone)"}, []bool{false, true}, []string{"", "Error! 'x' undefined function"}},
	{[]string{"(defun intro (x y) (list x this is y))", "(intro 'stanley 'livingstone)"}, []bool{false, true}, []string{"", "Error! this unassigned variable"}},
	{[]string{"(defun double (n) (* n 2))", "n"}, []bool{false, true}, []string{"", "Error! n unassigned variable"}},
	{[]string{"(defun test () (* 85 97))", "(test 1)"}, []bool{false, true}, []string{"", "Error! Too many arguments"}},
	{[]string{"(defun test () (* 85 97))", "test"}, []bool{false, true}, []string{"", "Error! test unassigned variable"}},
	{[]string{"(eval (eval (eval '''boing)))"}, []bool{true}, []string{"Error! boing unassigned variable"}},
	{[]string{"(defun double (n) (* n 2))", "(double 5)", "n"}, []bool{false, false, true}, []string{"nil", "10", "Error! n unassigned variable"}},
}

func TestGolispBad(t *testing.T) {
	for _, test := range baddata {
		i := Init()
		for j := range test.expression {
			log.Printf("TESTING %v", test.expression[j])
			e := Parse(test.expression[j])
			p, err := i.Eval(e.(Primitive), make([]Variable, 0))
			log.Printf("TESTING %v with %v", p, err)
			if test.fail[j] && err == nil {
				t.Errorf("Executing %v has not failed and it should have done: %v -> %v", e.str(), p, p.str())
			} else if !test.fail[j] && err != nil {
				t.Errorf("Executing %v has failed and it shouldn't have done: %v -> %v leads to %v", e.str(), p, p, err)
			}
			if err != nil && test.message[j] != "" {
				if test.message[j] != err.Error() {
					t.Errorf("Error messages don't match %v vs %v", test.message[j], err.Error())
				}
			}
		}
	}
}

func TestGolispGood(t *testing.T) {
	for _, test := range testdata {
		i := Init()
		for j := range test.expression {
			log.Printf("Running test on %v", test.expression[j])
			e := Parse(test.expression[j])
			r, err := i.Eval(e.(Primitive), make([]Variable, 0))
			if err != nil {
				t.Fatalf("Executing %v has failed for %v", e.str(), err)
			} else if r.str() != test.result[j] {
				t.Fatalf("%v did not lead to %v, it lead to %v with %v", test.expression[j], test.result[j], r.str(), test.expression)
			}
		}
	}
}
<|endoftext|>