<commit_before>package config

import (
	"reflect"
	"sync"
	"testing"

	"github.com/mholt/caddy/server"
)

func TestResolveAddr(t *testing.T) {
	// NOTE: If tests fail due to comparing to string "127.0.0.1",
	// it's possible that system env resolves with IPv6, or ::1.
	// If that happens, maybe we should use actualAddr.IP.IsLoopback()
	// for the assertion, rather than a direct string comparison.

	// NOTE: Tests with {Host: "", Port: ""} and {Host: "localhost", Port: ""}
	// will not behave the same cross-platform, so they have been omitted.

	for i, test := range []struct {
		config         server.Config
		shouldWarnErr  bool
		shouldFatalErr bool
		expectedIP     string
		expectedPort   int
	}{
		{server.Config{Host: "127.0.0.1", Port: "1234"}, false, false, "<nil>", 1234},
		{server.Config{Host: "localhost", Port: "80"}, false, false, "<nil>", 80},
		{server.Config{BindHost: "localhost", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "127.0.0.1", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "should-not-resolve", Port: "1234"}, true, false, "0.0.0.0", 1234},
		{server.Config{BindHost: "localhost", Port: "http"}, false, false, "127.0.0.1", 80},
		{server.Config{BindHost: "localhost", Port: "https"}, false, false, "127.0.0.1", 443},
		{server.Config{BindHost: "", Port: "1234"}, false, false, "<nil>", 1234},
		{server.Config{BindHost: "localhost", Port: "abcd"}, false, true, "", 0},
		{server.Config{BindHost: "127.0.0.1", Host: "should-not-be-used", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "localhost", Host: "should-not-be-used", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "should-not-resolve", Host: "localhost", Port: "1234"}, true, false, "0.0.0.0", 1234},
	} {
		actualAddr, warnErr, fatalErr := resolveAddr(test.config)

		if test.shouldFatalErr && fatalErr == nil {
			t.Errorf("Test %d: Expected error, but there wasn't any", i)
		}
		if !test.shouldFatalErr && fatalErr != nil {
			t.Errorf("Test %d: Expected no error, but there was one: %v", i, fatalErr)
		}
		if fatalErr != nil {
			continue
		}

		if test.shouldWarnErr && warnErr == nil {
			t.Errorf("Test %d: Expected warning, but there wasn't any", i)
		}
		if !test.shouldWarnErr && warnErr != nil {
			t.Errorf("Test %d: Expected no warning, but there was one: %v", i, warnErr)
		}

		if actual, expected := actualAddr.IP.String(), test.expectedIP; actual != expected {
			t.Errorf("Test %d: IP was %s but expected %s", i, actual, expected)
		}
		if actual, expected := actualAddr.Port, test.expectedPort; actual != expected {
			t.Errorf("Test %d: Port was %d but expected %d", i, actual, expected)
		}
	}
}

func TestMakeOnces(t *testing.T) {
	directives := []directive{
		{"dummy", nil},
		{"dummy2", nil},
	}
	directiveOrder = directives
	onces := makeOnces()
	if len(onces) != len(directives) {
		t.Errorf("onces had len %d , expected %d", len(onces), len(directives))
	}
	expected := map[string]*sync.Once{
		"dummy":  new(sync.Once),
		"dummy2": new(sync.Once),
	}
	if !reflect.DeepEqual(onces, expected) {
		t.Errorf("onces was %v, expected %v", onces, expected)
	}
}

func TestMakeStorages(t *testing.T) {
	directives := []directive{
		{"dummy", nil},
		{"dummy2", nil},
	}
	directiveOrder = directives
	storages := makeStorages()
	if len(storages) != len(directives) {
		t.Errorf("storages had len %d , expected %d", len(storages), len(directives))
	}
	expected := map[string]interface{}{
		"dummy":  nil,
		"dummy2": nil,
	}
	if !reflect.DeepEqual(storages, expected) {
		t.Errorf("storages was %v, expected %v", storages, expected)
	}
}

func TestValidDirective(t *testing.T) {
	directives := []directive{
		{"dummy", nil},
		{"dummy2", nil},
	}
	directiveOrder = directives
	for i, test := range []struct {
		directive string
		valid     bool
	}{
		{"dummy", true},
		{"dummy2", true},
		{"dummy3", false},
	} {
		if actual, expected := validDirective(test.directive), test.valid; actual != expected {
			t.Errorf("Test %d: valid was %t, expected %t", i, actual, expected)
		}
	}
}
<commit_msg>Add TestNewDefault to config tests<commit_after>package config

import (
	"reflect"
	"sync"
	"testing"

	"github.com/mholt/caddy/server"
)

func TestNewDefault(t *testing.T) {
	config := NewDefault()

	if actual, expected := config.Root, DefaultRoot; actual != expected {
		t.Errorf("Root was %s but expected %s", actual, expected)
	}
	if actual, expected := config.Host, DefaultHost; actual != expected {
		t.Errorf("Host was %s but expected %s", actual, expected)
	}
	if actual, expected := config.Port, DefaultPort; actual != expected {
		t.Errorf("Port was %d but expected %d", actual, expected)
	}
}

func TestResolveAddr(t *testing.T) {
	// NOTE: If tests fail due to comparing to string "127.0.0.1",
	// it's possible that system env resolves with IPv6, or ::1.
	// If that happens, maybe we should use actualAddr.IP.IsLoopback()
	// for the assertion, rather than a direct string comparison.

	// NOTE: Tests with {Host: "", Port: ""} and {Host: "localhost", Port: ""}
	// will not behave the same cross-platform, so they have been omitted.

	for i, test := range []struct {
		config         server.Config
		shouldWarnErr  bool
		shouldFatalErr bool
		expectedIP     string
		expectedPort   int
	}{
		{server.Config{Host: "127.0.0.1", Port: "1234"}, false, false, "<nil>", 1234},
		{server.Config{Host: "localhost", Port: "80"}, false, false, "<nil>", 80},
		{server.Config{BindHost: "localhost", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "127.0.0.1", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "should-not-resolve", Port: "1234"}, true, false, "0.0.0.0", 1234},
		{server.Config{BindHost: "localhost", Port: "http"}, false, false, "127.0.0.1", 80},
		{server.Config{BindHost: "localhost", Port: "https"}, false, false, "127.0.0.1", 443},
		{server.Config{BindHost: "", Port: "1234"}, false, false, "<nil>", 1234},
		{server.Config{BindHost: "localhost", Port: "abcd"}, false, true, "", 0},
		{server.Config{BindHost: "127.0.0.1", Host: "should-not-be-used", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "localhost", Host: "should-not-be-used", Port: "1234"}, false, false, "127.0.0.1", 1234},
		{server.Config{BindHost: "should-not-resolve", Host: "localhost", Port: "1234"}, true, false, "0.0.0.0", 1234},
	} {
		actualAddr, warnErr, fatalErr := resolveAddr(test.config)

		if test.shouldFatalErr && fatalErr == nil {
			t.Errorf("Test %d: Expected error, but there wasn't any", i)
		}
		if !test.shouldFatalErr && fatalErr != nil {
			t.Errorf("Test %d: Expected no error, but there was one: %v", i, fatalErr)
		}
		if fatalErr != nil {
			continue
		}

		if test.shouldWarnErr && warnErr == nil {
			t.Errorf("Test %d: Expected warning, but there wasn't any", i)
		}
		if !test.shouldWarnErr && warnErr != nil {
			t.Errorf("Test %d: Expected no warning, but there was one: %v", i, warnErr)
		}

		if actual, expected := actualAddr.IP.String(), test.expectedIP; actual != expected {
			t.Errorf("Test %d: IP was %s but expected %s", i, actual, expected)
		}
		if actual, expected := actualAddr.Port, test.expectedPort; actual != expected {
			t.Errorf("Test %d: Port was %d but expected %d", i, actual, expected)
		}
	}
}

func TestMakeOnces(t *testing.T) {
	directives := []directive{
		{"dummy", nil},
		{"dummy2", nil},
	}
	directiveOrder = directives
	onces := makeOnces()
	if len(onces) != len(directives) {
		t.Errorf("onces had len %d , expected %d", len(onces), len(directives))
	}
	expected := map[string]*sync.Once{
		"dummy":  new(sync.Once),
		"dummy2": new(sync.Once),
	}
	if !reflect.DeepEqual(onces, expected) {
		t.Errorf("onces was %v, expected %v", onces, expected)
	}
}

func TestMakeStorages(t *testing.T) {
	directives := []directive{
		{"dummy", nil},
		{"dummy2", nil},
	}
	directiveOrder = directives
	storages := makeStorages()
	if len(storages) != len(directives) {
		t.Errorf("storages had len %d , expected %d", len(storages), len(directives))
	}
	expected := map[string]interface{}{
		"dummy":  nil,
		"dummy2": nil,
	}
	if !reflect.DeepEqual(storages, expected) {
		t.Errorf("storages was %v, expected %v", storages, expected)
	}
}

func TestValidDirective(t *testing.T) {
	directives := []directive{
		{"dummy", nil},
		{"dummy2", nil},
	}
	directiveOrder = directives
	for i, test := range []struct {
		directive string
		valid     bool
	}{
		{"dummy", true},
		{"dummy2", true},
		{"dummy3", false},
	} {
		if actual, expected := validDirective(test.directive), test.valid; actual != expected {
			t.Errorf("Test %d: valid was %t, expected %t", i, actual, expected)
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var sampleConfig = `
apikey = "abcde"

[connection]
post_metrics_retry_max = 5

[plugin.metrics.mysql]
command = "ruby /path/to/your/plugin/mysql.rb"

[sensu.checks.memory] # for backward compatibility
command = "ruby ../sensu/plugins/system/memory-metrics.rb"
type = "metric"
`

func TestLoadConfig(t *testing.T) {
	tmpFile, error := ioutil.TempFile("/tmp", "")
	if error != nil {
		t.Error("should not raise error")
	}
	if err := ioutil.WriteFile(tmpFile.Name(), []byte(sampleConfig), 0644); err != nil {
		t.Error("should not raise error")
	}

	config, err := LoadConfig(tmpFile.Name())
	if err != nil {
		t.Error("should not raise error")
	}

	if config.Apibase != "https://mackerel.io" {
		t.Error("should be https://mackerel.io (arg value should be used)")
	}

	if config.Apikey != "abcde" {
		t.Error("should be abcde (config value should be used)")
	}

	if config.Connection.Post_Metrics_Dequeue_Delay_Seconds != 30 {
		t.Error("should be 30 (default value should be used)")
	}

	if config.Connection.Post_Metrics_Retry_Max != 5 {
		t.Error("should be 5 (config value should be used)")
	}
}

func TestLoadConfigFile(t *testing.T) {
	tmpFile, error := ioutil.TempFile("", "mackerel-config-test")
	if error != nil {
		t.Error("should not raise error")
	}
	if _, err := tmpFile.WriteString(sampleConfig); err != nil {
		t.Fatal("should not raise error")
	}
	tmpFile.Sync()
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	config, err := LoadConfigFile(tmpFile.Name())
	if err != nil {
		t.Error("should not raise error")
	}

	if config.Apikey != "abcde" {
		t.Error("Apikey should be abcde")
	}

	if config.Connection.Post_Metrics_Retry_Max != 5 {
		t.Error("Post_Metrics_Retry_Max should be 5")
	}

	if config.Plugin["metrics"] == nil {
		t.Error("plugin should have metrics")
	}
	pluginConf := config.Plugin["metrics"]["mysql"]
	if pluginConf.Command != "ruby /path/to/your/plugin/mysql.rb" {
		t.Errorf("plugin conf command should be 'ruby /path/to/your/plugin/mysql.rb' but %v", pluginConf.Command)
	}

	// for backward compatibility
	sensu := config.Plugin["metrics"]["DEPRECATED-sensu-memory"]
	if sensu.Command != "ruby ../sensu/plugins/system/memory-metrics.rb" {
		t.Error("sensu command should be 'ruby ../sensu/plugins/system/memory-metrics.rb'")
	}
}

func assertNoError(t *testing.T, err error) {
	if err != nil {
		t.Error(err)
	}
}

func assert(t *testing.T, ok bool, msg string) {
	if !ok {
		t.Error(msg)
	}
}

func TestLoadConfigFileInclude(t *testing.T) {
	configDir, err := ioutil.TempDir("", "mackerel-config-test")
	assertNoError(t, err)

	configFile, err := ioutil.TempFile("", "mackerel-config-test")
	assertNoError(t, err)

	includedFile, err := os.Create(filepath.Join(configDir, "sub1.conf"))

	configContent := fmt.Sprintf(`
apikey = "not overwritten"
roles = [ "roles", "to be overwritten" ]

include = "%s/*.conf"

[plugin.metrics.foo1]
command = "foo1"
`, configDir)

	includedContent := `
roles = [ "Service:role" ]

[plugin.metrics.foo2]
command = "foo2"
`

	_, err = configFile.WriteString(configContent)
	assertNoError(t, err)

	_, err = includedFile.WriteString(includedContent)
	assertNoError(t, err)

	configFile.Close()
	includedFile.Close()
	defer os.Remove(configFile.Name())
	defer os.Remove(includedFile.Name())

	config, err := LoadConfigFile(configFile.Name())
	assertNoError(t, err)

	assert(t, config.Apikey == "not overwritten", "apikey should not be overwritten")
	assert(t, len(config.Roles) == 1, "roles should be overwritten")
	assert(t, config.Roles[0] == "Service:role", "roles should be overwritten")
	assert(t, config.Plugin["metrics"]["foo1"].Command == "foo1", "plugin.metrics.foo1 should exist")
	assert(t, config.Plugin["metrics"]["foo2"].Command == "foo2", "plugin.metrics.foo2 should exist")
}
<commit_msg>test merging plugin keys<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var sampleConfig = `
apikey = "abcde"

[connection]
post_metrics_retry_max = 5

[plugin.metrics.mysql]
command = "ruby /path/to/your/plugin/mysql.rb"

[sensu.checks.memory] # for backward compatibility
command = "ruby ../sensu/plugins/system/memory-metrics.rb"
type = "metric"
`

func TestLoadConfig(t *testing.T) {
	tmpFile, error := ioutil.TempFile("/tmp", "")
	if error != nil {
		t.Error("should not raise error")
	}
	if err := ioutil.WriteFile(tmpFile.Name(), []byte(sampleConfig), 0644); err != nil {
		t.Error("should not raise error")
	}

	config, err := LoadConfig(tmpFile.Name())
	if err != nil {
		t.Error("should not raise error")
	}

	if config.Apibase != "https://mackerel.io" {
		t.Error("should be https://mackerel.io (arg value should be used)")
	}

	if config.Apikey != "abcde" {
		t.Error("should be abcde (config value should be used)")
	}

	if config.Connection.Post_Metrics_Dequeue_Delay_Seconds != 30 {
		t.Error("should be 30 (default value should be used)")
	}

	if config.Connection.Post_Metrics_Retry_Max != 5 {
		t.Error("should be 5 (config value should be used)")
	}
}

func TestLoadConfigFile(t *testing.T) {
	tmpFile, error := ioutil.TempFile("", "mackerel-config-test")
	if error != nil {
		t.Error("should not raise error")
	}
	if _, err := tmpFile.WriteString(sampleConfig); err != nil {
		t.Fatal("should not raise error")
	}
	tmpFile.Sync()
	tmpFile.Close()
	defer os.Remove(tmpFile.Name())

	config, err := LoadConfigFile(tmpFile.Name())
	if err != nil {
		t.Error("should not raise error")
	}

	if config.Apikey != "abcde" {
		t.Error("Apikey should be abcde")
	}

	if config.Connection.Post_Metrics_Retry_Max != 5 {
		t.Error("Post_Metrics_Retry_Max should be 5")
	}

	if config.Plugin["metrics"] == nil {
		t.Error("plugin should have metrics")
	}
	pluginConf := config.Plugin["metrics"]["mysql"]
	if pluginConf.Command != "ruby /path/to/your/plugin/mysql.rb" {
		t.Errorf("plugin conf command should be 'ruby /path/to/your/plugin/mysql.rb' but %v", pluginConf.Command)
	}

	// for backward compatibility
	sensu := config.Plugin["metrics"]["DEPRECATED-sensu-memory"]
	if sensu.Command != "ruby ../sensu/plugins/system/memory-metrics.rb" {
		t.Error("sensu command should be 'ruby ../sensu/plugins/system/memory-metrics.rb'")
	}
}

func assertNoError(t *testing.T, err error) {
	if err != nil {
		t.Error(err)
	}
}

func assert(t *testing.T, ok bool, msg string) {
	if !ok {
		t.Error(msg)
	}
}

func TestLoadConfigFileInclude(t *testing.T) {
	configDir, err := ioutil.TempDir("", "mackerel-config-test")
	assertNoError(t, err)

	configFile, err := ioutil.TempFile("", "mackerel-config-test")
	assertNoError(t, err)

	includedFile, err := os.Create(filepath.Join(configDir, "sub1.conf"))

	configContent := fmt.Sprintf(`
apikey = "not overwritten"
roles = [ "roles", "to be overwritten" ]

include = "%s/*.conf"

[plugin.metrics.foo1]
command = "foo1"

[plugin.metrics.bar]
command = "this wille be overwritten"
`, configDir)

	includedContent := `
roles = [ "Service:role" ]

[plugin.metrics.foo2]
command = "foo2"

[plugin.metrics.bar]
command = "bar"
`

	_, err = configFile.WriteString(configContent)
	assertNoError(t, err)

	_, err = includedFile.WriteString(includedContent)
	assertNoError(t, err)

	configFile.Close()
	includedFile.Close()
	defer os.Remove(configFile.Name())
	defer os.Remove(includedFile.Name())

	config, err := LoadConfigFile(configFile.Name())
	assertNoError(t, err)

	assert(t, config.Apikey == "not overwritten", "apikey should not be overwritten")
	assert(t, len(config.Roles) == 1, "roles should be overwritten")
	assert(t, config.Roles[0] == "Service:role", "roles should be overwritten")
	assert(t, config.Plugin["metrics"]["foo1"].Command == "foo1", "plugin.metrics.foo1 should exist")
	assert(t, config.Plugin["metrics"]["foo2"].Command == "foo2", "plugin.metrics.foo2 should exist")
	assert(t, config.Plugin["metrics"]["bar"].Command == "bar", "plugin.metrics.bar should be overwritten")
}
<|endoftext|>


<commit_before>package cpu6502

import (
	"io/ioutil"
	"testing"
)

type CpuTest struct {
	Name string
	Mem  []byte
	End  Cpu
}

type Ram []byte

func (r Ram) Read(v uint16) byte     { return r[v] }
func (r Ram) Write(v uint16, b byte) { r[v] = b }

var CpuTests = []CpuTest{
	{
		Name: "load, set",
		Mem:  []byte{0xa9, 0x01, 0x8d, 0x00, 0x02, 0xa9, 0x05, 0x8d, 0x01, 0x02, 0xa9, 0x08, 0x8d, 0x02, 0x02},
		End: Cpu{
			A:  0x08,
			S:  0xff,
			PC: 0x0610,
			P:  0x30,
		},
	},
	{
		Name: "load, transfer, increment, add",
		Mem:  []byte{0xa9, 0xc0, 0xaa, 0xe8, 0x69, 0xc4, 0x00},
		End: Cpu{
			A:  0x84,
			X:  0xc1,
			S:  0xff,
			PC: 0x0607,
			P:  0xb1,
		},
	},
	{
		Name: "bne",
		Mem:  []byte{0xa2, 0x08, 0xca, 0x8e, 0x00, 0x02, 0xe0, 0x03, 0xd0, 0xf8, 0x8e, 0x01, 0x02, 0x00},
		End: Cpu{
			X:  0x03,
			S:  0xff,
			PC: 0x060e,
			P:  0x33,
		},
	},
	{
		Name: "relative",
		Mem:  []byte{0xa9, 0x01, 0xc9, 0x02, 0xd0, 0x02, 0x85, 0x22, 0x00},
		End: Cpu{
			A:  0x01,
			S:  0xff,
			PC: 0x0609,
			P:  0xb0,
		},
	},
	{
		Name: "indirect",
		Mem:  []byte{0xa9, 0x01, 0x85, 0xf0, 0xa9, 0xcc, 0x85, 0xf1, 0x6c, 0xf0, 0x00},
		End: Cpu{
			A:  0xcc,
			S:  0xff,
			PC: 0xcc02,
			P:  0xb0,
		},
	},
	{
		Name: "indexed indirect",
		Mem:  []byte{0xa2, 0x01, 0xa9, 0x05, 0x85, 0x01, 0xa9, 0x06, 0x85, 0x02, 0xa0, 0x0a, 0x8c, 0x05, 0x06, 0xa1, 0x00},
		End: Cpu{
			A:  0x0a,
			X:  0x01,
			Y:  0x0a,
			S:  0xff,
			PC: 0x0612,
			P:  0x30,
		},
	},
	{
		Name: "indirect indexed",
		Mem:  []byte{0xa0, 0x01, 0xa9, 0x03, 0x85, 0x01, 0xa9, 0x07, 0x85, 0x02, 0xa2, 0x0a, 0x8e, 0x04, 0x07, 0xb1, 0x01},
		End: Cpu{
			A:  0x0a,
			X:  0x0a,
			Y:  0x01,
			S:  0xff,
			PC: 0x0612,
			P:  0x30,
		},
	},
	{
		Name: "stack",
		Mem:  []byte{0xa2, 0x00, 0xa0, 0x00, 0x8a, 0x99, 0x00, 0x02, 0x48, 0xe8, 0xc8, 0xc0, 0x10, 0xd0, 0xf5, 0x68, 0x99, 0x00, 0x02, 0xc8, 0xc0, 0x20, 0xd0, 0xf7},
		End: Cpu{
			X:  0x10,
			Y:  0x20,
			S:  0xff,
			PC: 0x0619,
			P:  0x33,
		},
	},
	{
		Name: "jsr/rts",
		Mem:  []byte{0x20, 0x09, 0x06, 0x20, 0x0c, 0x06, 0x20, 0x12, 0x06, 0xa2, 0x00, 0x60, 0xe8, 0xe0, 0x05, 0xd0, 0xfb, 0x60, 0x00},
		End: Cpu{
			X:  0x05,
			S:  0xfd,
			PC: 0x0613,
			P:  0x33,
		},
	},
	{
		Name: "others",
		Mem:  []byte{0xa9, 0x30, 0x29, 0x9f, 0x0a, 0xa2, 0x0f, 0x86, 0x00, 0x06, 0x00, 0xa4, 0x00, 0x24, 0x00},
		End: Cpu{
			A:  0x20,
			X:  0x0f,
			Y:  0x1e,
			S:  0xff,
			PC: 0x0610,
			P:  0x32,
		},
	},
	{
		Name: "trb1",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x33, 0x14, 0x00},
		End: Cpu{
			A:  0x33,
			S:  0xff,
			PC: 0x0609,
			P:  0x30,
		},
	},
	{
		Name: "trb2",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x41, 0x14, 0x00},
		End: Cpu{
			A:  0x41,
			S:  0xff,
			PC: 0x0609,
			P:  0x32,
		},
	},
	{
		Name: "tsb1",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x33, 0x04, 0x00},
		End: Cpu{
			A:  0x33,
			S:  0xff,
			PC: 0x0609,
			P:  0x30,
		},
	},
	{
		Name: "tsb2",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x41, 0x04, 0x00},
		End: Cpu{
			A:  0x41,
			S:  0xff,
			PC: 0x0609,
			P:  0x32,
		},
	},
}

func Test6502(t *testing.T) {
	for _, test := range CpuTests {
		r := make(Ram, 0xffff+1)
		c := New(r)
		copy(r[c.PC:], test.Mem)
		c.Run()
		if c.A != test.End.A ||
			c.X != test.End.X ||
			c.Y != test.End.Y ||
			c.S != test.End.S ||
			c.PC != test.End.PC ||
			c.P != test.End.P {
			t.Fatalf("bad cpu state %s, got:\n%sexpected:\n%s", test.Name, c, &test.End)
		}
	}
}

// Download from https://github.com/Klaus2m5/6502_65C02_functional_tests/blob/master/bin_files/6502_functional_test.bin
// GPL, so not included here.
func TestFunctional(t *testing.T) {
	b, err := ioutil.ReadFile("6502_functional_test.bin")
	if err != nil {
		t.Fatal(err)
	}
	r := make(Ram, 0xffff+1)
	copy(r[:], b)
	c := New(r)
	c.PC = 0x0400
	for !c.Halt {
		pc := c.PC
		c.Step()
		if c.PC == pc {
			t.Fatal()
		}
	}
}
<commit_msg>BRK is two bytes, incr PC<commit_after>package cpu6502

import (
	"io/ioutil"
	"testing"
)

type CpuTest struct {
	Name string
	Mem  []byte
	End  Cpu
}

type Ram []byte

func (r Ram) Read(v uint16) byte     { return r[v] }
func (r Ram) Write(v uint16, b byte) { r[v] = b }

var CpuTests = []CpuTest{
	{
		Name: "load, set",
		Mem:  []byte{0xa9, 0x01, 0x8d, 0x00, 0x02, 0xa9, 0x05, 0x8d, 0x01, 0x02, 0xa9, 0x08, 0x8d, 0x02, 0x02},
		End: Cpu{
			A:  0x08,
			S:  0xff,
			PC: 0x0611,
			P:  0x30,
		},
	},
	{
		Name: "load, transfer, increment, add",
		Mem:  []byte{0xa9, 0xc0, 0xaa, 0xe8, 0x69, 0xc4, 0x00},
		End: Cpu{
			A:  0x84,
			X:  0xc1,
			S:  0xff,
			PC: 0x0608,
			P:  0xb1,
		},
	},
	{
		Name: "bne",
		Mem:  []byte{0xa2, 0x08, 0xca, 0x8e, 0x00, 0x02, 0xe0, 0x03, 0xd0, 0xf8, 0x8e, 0x01, 0x02, 0x00},
		End: Cpu{
			X:  0x03,
			S:  0xff,
			PC: 0x060f,
			P:  0x33,
		},
	},
	{
		Name: "relative",
		Mem:  []byte{0xa9, 0x01, 0xc9, 0x02, 0xd0, 0x02, 0x85, 0x22, 0x00},
		End: Cpu{
			A:  0x01,
			S:  0xff,
			PC: 0x060a,
			P:  0xb0,
		},
	},
	{
		Name: "indirect",
		Mem:  []byte{0xa9, 0x01, 0x85, 0xf0, 0xa9, 0xcc, 0x85, 0xf1, 0x6c, 0xf0, 0x00},
		End: Cpu{
			A:  0xcc,
			S:  0xff,
			PC: 0xcc03,
			P:  0xb0,
		},
	},
	{
		Name: "indexed indirect",
		Mem:  []byte{0xa2, 0x01, 0xa9, 0x05, 0x85, 0x01, 0xa9, 0x06, 0x85, 0x02, 0xa0, 0x0a, 0x8c, 0x05, 0x06, 0xa1, 0x00},
		End: Cpu{
			A:  0x0a,
			X:  0x01,
			Y:  0x0a,
			S:  0xff,
			PC: 0x0613,
			P:  0x30,
		},
	},
	{
		Name: "indirect indexed",
		Mem:  []byte{0xa0, 0x01, 0xa9, 0x03, 0x85, 0x01, 0xa9, 0x07, 0x85, 0x02, 0xa2, 0x0a, 0x8e, 0x04, 0x07, 0xb1, 0x01},
		End: Cpu{
			A:  0x0a,
			X:  0x0a,
			Y:  0x01,
			S:  0xff,
			PC: 0x0613,
			P:  0x30,
		},
	},
	{
		Name: "stack",
		Mem:  []byte{0xa2, 0x00, 0xa0, 0x00, 0x8a, 0x99, 0x00, 0x02, 0x48, 0xe8, 0xc8, 0xc0, 0x10, 0xd0, 0xf5, 0x68, 0x99, 0x00, 0x02, 0xc8, 0xc0, 0x20, 0xd0, 0xf7},
		End: Cpu{
			X:  0x10,
			Y:  0x20,
			S:  0xff,
			PC: 0x061a,
			P:  0x33,
		},
	},
	{
		Name: "jsr/rts",
		Mem:  []byte{0x20, 0x09, 0x06, 0x20, 0x0c, 0x06, 0x20, 0x12, 0x06, 0xa2, 0x00, 0x60, 0xe8, 0xe0, 0x05, 0xd0, 0xfb, 0x60, 0x00},
		End: Cpu{
			X:  0x05,
			S:  0xfd,
			PC: 0x0614,
			P:  0x33,
		},
	},
	{
		Name: "others",
		Mem:  []byte{0xa9, 0x30, 0x29, 0x9f, 0x0a, 0xa2, 0x0f, 0x86, 0x00, 0x06, 0x00, 0xa4, 0x00, 0x24, 0x00},
		End: Cpu{
			A:  0x20,
			X:  0x0f,
			Y:  0x1e,
			S:  0xff,
			PC: 0x0611,
			P:  0x32,
		},
	},
	{
		Name: "trb1",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x33, 0x14, 0x00},
		End: Cpu{
			A:  0x33,
			S:  0xff,
			PC: 0x060a,
			P:  0x30,
		},
	},
	{
		Name: "trb2",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x41, 0x14, 0x00},
		End: Cpu{
			A:  0x41,
			S:  0xff,
			PC: 0x060a,
			P:  0x32,
		},
	},
	{
		Name: "tsb1",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x33, 0x04, 0x00},
		End: Cpu{
			A:  0x33,
			S:  0xff,
			PC: 0x060a,
			P:  0x30,
		},
	},
	{
		Name: "tsb2",
		Mem:  []byte{0xa9, 0xa6, 0x85, 0x00, 0xa9, 0x41, 0x04, 0x00},
		End: Cpu{
			A:  0x41,
			S:  0xff,
			PC: 0x060a,
			P:  0x32,
		},
	},
}

func Test6502(t *testing.T) {
	for _, test := range CpuTests {
		r := make(Ram, 0xffff+1)
		c := New(r)
		copy(r[c.PC:], test.Mem)
		c.Run()
		if c.A != test.End.A ||
			c.X != test.End.X ||
			c.Y != test.End.Y ||
			c.S != test.End.S ||
			c.PC != test.End.PC ||
			c.P != test.End.P {
			t.Fatalf("bad cpu state %s, got:\n%sexpected:\n%s", test.Name, c, &test.End)
		}
	}
}

// Download from https://github.com/Klaus2m5/6502_65C02_functional_tests/blob/master/bin_files/6502_functional_test.bin
// GPL, so not included here.
func TestFunctional(t *testing.T) {
	b, err := ioutil.ReadFile("6502_functional_test.bin")
	if err != nil {
		t.Fatal(err)
	}
	r := make(Ram, 0xffff+1)
	copy(r[:], b)
	c := New(r)
	c.PC = 0x0400
	for !c.Halt {
		pc := c.PC
		c.Step()
		if c.PC == pc {
			t.Fatal()
		}
	}
}
<|endoftext|>


<commit_before>package crawler

import (
	"net"
	"net/url"
	// "path"
	"context"
	"fmt"
	"github.com/ipfs-search/ipfs-search/indexer"
	"github.com/ipfs/go-ipfs-api"
	"log"
	"strings"
	"syscall"
	"time"
)

// Indexable consists of args with a Crawler
type Indexable struct {
	*Crawler
	*Args
}

// String returns '<hash>' (<name>)
func (i *Indexable) String() string {
	if i.Name != "" {
		return fmt.Sprintf("'%s' (%s)", i.Hash, i.Name)
	}
	return fmt.Sprintf("'%s' (Unnamed)", i.Hash)
}

// handleShellError handles IPFS shell errors; returns try again bool and original error
func (i *Indexable) handleShellError(ctx context.Context, err error) (bool, error) {
	if _, ok := err.(*shell.Error); ok && (strings.Contains(err.Error(), "proto") ||
		strings.Contains(err.Error(), "unrecognized type") ||
		strings.Contains(err.Error(), "not a valid merkledag node")) {

		// Attempt to index invalid to prevent re-indexing
		i.indexInvalid(ctx, err)

		// Don't try again, return error
		return false, err
	}

	// Different error, attempt handling as URL error
	return i.handleURLError(err)
}

// handleURLError handles HTTP errors graceously, returns try again bool and original error
func (i *Indexable) handleURLError(err error) (bool, error) {
	if uerr, ok := err.(*url.Error); ok {
		if uerr.Timeout() {
			// Fail on timeouts
			return false, err
		}

		if uerr.Temporary() {
			// Retry on other temp errors
			log.Printf("Temporary URL error: %v", uerr)
			return true, nil
		}

		// Somehow, the errors below are not temp errors !?
		switch t := uerr.Err.(type) {
		case *net.OpError:
			if t.Op == "dial" {
				log.Printf("Unknown host %v", t)
				return true, nil

			} else if t.Op == "read" {
				log.Printf("Connection refused %v", t)
				return true, nil
			}

		case syscall.Errno:
			if t == syscall.ECONNREFUSED {
				log.Printf("Connection refused %v", t)
				return true, nil
			}
		}
	}

	return false, err
}

// hashURL returns the IPFS URL for a particular hash
func (i *Indexable) hashURL() string {
	return fmt.Sprintf("/ipfs/%s", i.Hash)
}

// getFileList return list of files and/or type of item (directory/file)
func (i *Indexable) getFileList(ctx context.Context) (list *shell.UnixLsObject, err error) {
	url := i.hashURL()

	tryAgain := true
	for tryAgain {
		list, err = i.Shell.FileList(url)

		tryAgain, err = i.handleShellError(ctx, err)

		if tryAgain {
			log.Printf("Retrying in %s", i.Config.RetryWait)
			time.Sleep(i.Config.RetryWait)
		}
	}

	return
}

// indexInvalid indexes invalid files to prevent indexing again
func (i *Indexable) indexInvalid(ctx context.Context, err error) {
	// Attempt to index panic to prevent re-indexing
	m := metadata{
		"error": err.Error(),
	}

	i.Indexer.IndexItem(ctx, "invalid", i.Hash, m)
}

// queueList queues any items in a given list/directory
func (i *Indexable) queueList(ctx context.Context, list *shell.UnixLsObject) (err error) {
	for _, link := range list.Links {
		dirArgs := &Args{
			Hash:       link.Hash,
			Name:       link.Name,
			Size:       link.Size,
			ParentHash: i.Hash,
		}

		switch link.Type {
		case "File":
			// Add file to crawl queue
			err = i.FileQueue.Publish(dirArgs)
		case "Directory":
			// Add directory to crawl queue
			err = i.HashQueue.Publish(dirArgs)
		default:
			log.Printf("Type '%s' skipped for %s", link.Type, i)
			i.indexInvalid(ctx, fmt.Errorf("Unknown type: %s", link.Type))
		}
	}

	return
}

// processList processes and indexes a file listing
func (i *Indexable) processList(ctx context.Context, list *shell.UnixLsObject, references []indexer.Reference) (err error) {
	now := nowISO()

	switch list.Type {
	case "File":
		// Add to file crawl queue
		fileArgs := &Args{
			Hash:       i.Hash,
			Name:       i.Name,
			Size:       list.Size,
			ParentHash: i.ParentHash,
		}

		err = i.FileQueue.Publish(fileArgs)
	case "Directory":
		// Queue indexing of linked items
		err = i.queueList(ctx, list)
		if err != nil {
			return err
		}

		// Index name and size for directory and directory items
		m := metadata{
			"links":      list.Links,
			"size":       list.Size,
			"references": references,
			"first-seen": now,
			"last-seen":  now,
		}

		err = i.Indexer.IndexItem(ctx, "directory", i.Hash, m)
	default:
		log.Printf("Type '%s' skipped for %s", list.Type, i)
	}

	return
}

// processList processes and indexes a single file
func (i *Indexable) processFile(ctx context.Context, references []indexer.Reference) error {
	now := nowISO()

	m := make(metadata)

	err := i.getMetadata(&m)
	if err != nil {
		return err
	}

	// Add previously found references now
	m["size"] = i.Size
	m["references"] = references
	m["first-seen"] = now
	m["last-seen"] = now

	return i.Indexer.IndexItem(ctx, "file", i.Hash, m)
}

// preCrawl checks for and returns existing item and conditionally updates it
func (i *Indexable) preCrawl(ctx context.Context) (*existingItem, error) {
	e, err := i.getExistingItem(ctx)
	if err != nil {
		return nil, err
	}

	return e, e.update(ctx)
}

// CrawlHash crawls a particular hash (file or directory)
func (i *Indexable) CrawlHash(ctx context.Context) error {
	existing, err := i.preCrawl(ctx)

	if err != nil || !existing.shouldCrawl() {
		log.Printf("Skipping hash %s", i)
		return err
	}

	log.Printf("Crawling hash %s", i)

	list, err := i.getFileList(ctx)
	if err != nil {
		return err
	}

	err = i.processList(ctx, list, existing.references)
	if err != nil {
		return err
	}

	log.Printf("Finished hash %s", i)

	return nil
}

// CrawlFile crawls a single object, known to be a file
func (i *Indexable) CrawlFile(ctx context.Context) error {
	existing, err := i.preCrawl(ctx)

	if err != nil || !existing.shouldCrawl() {
		log.Printf("Skipping file %s", i)
		return err
	}

	log.Printf("Crawling file %s", i)

	i.processFile(ctx, existing.references)
	if err != nil {
		return err
	}

	log.Printf("Finished file %s", i)

	return nil
}
<commit_msg>Imports cleanup.<commit_after>package crawler

import (
	"context"
	"fmt"
	"github.com/ipfs-search/ipfs-search/indexer"
	"github.com/ipfs/go-ipfs-api"
	"log"
	"net"
	"net/url"
	"strings"
	"syscall"
	"time"
)

// Indexable consists of args with a Crawler
type Indexable struct {
	*Crawler
	*Args
}

// String returns '<hash>' (<name>)
func (i *Indexable) String() string {
	if i.Name != "" {
		return fmt.Sprintf("'%s' (%s)", i.Hash, i.Name)
	}
	return fmt.Sprintf("'%s' (Unnamed)", i.Hash)
}

// handleShellError handles IPFS shell errors; returns try again bool and original error
func (i *Indexable) handleShellError(ctx context.Context, err error) (bool, error) {
	if _, ok := err.(*shell.Error); ok && (strings.Contains(err.Error(), "proto") ||
		strings.Contains(err.Error(), "unrecognized type") ||
		strings.Contains(err.Error(), "not a valid merkledag node")) {

		// Attempt to index invalid to prevent re-indexing
		i.indexInvalid(ctx, err)

		// Don't try again, return error
		return false, err
	}

	// Different error, attempt handling as URL error
	return i.handleURLError(err)
}

// handleURLError handles HTTP errors graceously, returns try again bool and original error
func (i *Indexable) handleURLError(err error) (bool, error) {
	if uerr, ok := err.(*url.Error); ok {
		if uerr.Timeout() {
			// Fail on timeouts
			return false, err
		}

		if uerr.Temporary() {
			// Retry on other temp errors
			log.Printf("Temporary URL error: %v", uerr)
			return true, nil
		}

		// Somehow, the errors below are not temp errors !?
		switch t := uerr.Err.(type) {
		case *net.OpError:
			if t.Op == "dial" {
				log.Printf("Unknown host %v", t)
				return true, nil

			} else if t.Op == "read" {
				log.Printf("Connection refused %v", t)
				return true, nil
			}

		case syscall.Errno:
			if t == syscall.ECONNREFUSED {
				log.Printf("Connection refused %v", t)
				return true, nil
			}
		}
	}

	return false, err
}

// hashURL returns the IPFS URL for a particular hash
func (i *Indexable) hashURL() string {
	return fmt.Sprintf("/ipfs/%s", i.Hash)
}

// getFileList return list of files and/or type of item (directory/file)
func (i *Indexable) getFileList(ctx context.Context) (list *shell.UnixLsObject, err error) {
	url := i.hashURL()

	tryAgain := true
	for tryAgain {
		list, err = i.Shell.FileList(url)

		tryAgain, err = i.handleShellError(ctx, err)

		if tryAgain {
			log.Printf("Retrying in %s", i.Config.RetryWait)
			time.Sleep(i.Config.RetryWait)
		}
	}

	return
}

// indexInvalid indexes invalid files to prevent indexing again
func (i *Indexable) indexInvalid(ctx context.Context, err error) {
	// Attempt to index panic to prevent re-indexing
	m := metadata{
		"error": err.Error(),
	}

	i.Indexer.IndexItem(ctx, "invalid", i.Hash, m)
}

// queueList queues any items in a given list/directory
func (i *Indexable) queueList(ctx context.Context, list *shell.UnixLsObject) (err error) {
	for _, link := range list.Links {
		dirArgs := &Args{
			Hash:       link.Hash,
			Name:       link.Name,
			Size:       link.Size,
			ParentHash: i.Hash,
		}

		switch link.Type {
		case "File":
			// Add file to crawl queue
			err = i.FileQueue.Publish(dirArgs)
		case "Directory":
			// Add directory to crawl queue
			err = i.HashQueue.Publish(dirArgs)
		default:
			log.Printf("Type '%s' skipped for %s", link.Type, i)
			i.indexInvalid(ctx, fmt.Errorf("Unknown type: %s", link.Type))
		}
	}

	return
}

// processList processes and indexes a file listing
func (i *Indexable) processList(ctx context.Context, list *shell.UnixLsObject, references []indexer.Reference) (err error) {
	now := nowISO()

	switch list.Type {
	case "File":
		// Add to file crawl queue
		fileArgs := &Args{
			Hash:       i.Hash,
			Name:       i.Name,
			Size:       list.Size,
			ParentHash: i.ParentHash,
		}

		err = i.FileQueue.Publish(fileArgs)
	case "Directory":
		// Queue indexing of linked items
		err = i.queueList(ctx, list)
		if err != nil {
			return err
		}

		// Index name and size for directory and directory items
		m := metadata{
			"links":      list.Links,
			"size":       list.Size,
			"references": references,
			"first-seen": now,
			"last-seen":  now,
		}

		err = i.Indexer.IndexItem(ctx, "directory", i.Hash, m)
	default:
		log.Printf("Type '%s' skipped for %s", list.Type, i)
	}

	return
}

// processList processes and indexes a single file
func (i *Indexable) processFile(ctx context.Context, references []indexer.Reference) error {
	now := nowISO()

	m := make(metadata)

	err := i.getMetadata(&m)
	if err != nil {
		return err
	}

	// Add previously found references now
	m["size"] = i.Size
	m["references"] = references
	m["first-seen"] = now
	m["last-seen"] = now

	return i.Indexer.IndexItem(ctx, "file", i.Hash, m)
}

// preCrawl checks for and returns existing item and conditionally updates it
func (i *Indexable) preCrawl(ctx context.Context) (*existingItem, error) {
	e, err := i.getExistingItem(ctx)
	if err != nil {
		return nil, err
	}

	return e, e.update(ctx)
}

// CrawlHash crawls a particular hash (file or directory)
func (i *Indexable) CrawlHash(ctx context.Context) error {
	existing, err := i.preCrawl(ctx)

	if err != nil || !existing.shouldCrawl() {
		log.Printf("Skipping hash %s", i)
		return err
	}

	log.Printf("Crawling hash %s", i)

	list, err := i.getFileList(ctx)
	if err != nil {
		return err
	}

	err = i.processList(ctx, list, existing.references)
	if err != nil {
		return err
	}

	log.Printf("Finished hash %s", i)

	return nil
}

// CrawlFile crawls a single object, known to be a file
func (i *Indexable) CrawlFile(ctx context.Context) error {
	existing, err := i.preCrawl(ctx)

	if err != nil || !existing.shouldCrawl() {
		log.Printf("Skipping file %s", i)
		return err
	}

	log.Printf("Crawling file %s", i)

	i.processFile(ctx, existing.references)
	if err != nil {
		return err
	}

	log.Printf("Finished file %s", i)

	return nil
}
<|endoftext|>


<commit_before>package sudoku

import (
	"log"
	"testing"
)

func TestSubsetIndexes(t *testing.T) {
	result := subsetIndexes(3, 1)
	expectedResult := [][]int{[]int{0}, []int{1}, []int{2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(3, 2)
	expectedResult = [][]int{[]int{0, 1}, []int{0, 2}, []int{1, 2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(5, 3)
	expectedResult = [][]int{[]int{0, 1, 2}, []int{0, 1, 3}, []int{0, 1, 4}, []int{0, 2, 3}, []int{0, 2, 4}, []int{0, 3, 4}, []int{1, 2, 3}, []int{1, 2, 4}, []int{1, 3, 4}, []int{2, 3, 4}}
	subsetIndexHelper(t, result, expectedResult)

	if subsetIndexes(1, 2) != nil {
		t.Log("Subset indexes returned a subset where the length is greater than the len")
		t.Fail()
	}

}

func subsetIndexHelper(t *testing.T, result [][]int, expectedResult [][]int) {
	if len(result) != len(expectedResult) {
		t.Log("subset indexes returned wrong number of results for: ", result, " :", expectedResult)
		t.FailNow()
	}
	for i, item := range result {
		if len(item) != len(expectedResult[0]) {
			t.Log("subset indexes returned a result with wrong numbrer of items ", i, " : ", result, " : ", expectedResult)
			t.FailNow()
		}
		for j, value := range item {
			if value != expectedResult[i][j] {
				t.Log("Subset indexes had wrong number at ", i, ",", j, " : ", result, " : ", expectedResult)
				t.Fail()
			}
		}
	}
}

type solveTechniqueMatchMode int

const (
	solveTechniqueMatchModeAll = iota
	solveTechniqueMatchModeAny
)

type solveTechniqueTestHelperOptions struct {
	transpose bool
	//Whether the descriptions of cells are a list of legal possible individual values, or must all match.
	matchMode    solveTechniqueMatchMode
	targetCells  []cellRef
	pointerCells []cellRef
	targetNums   IntSlice
	pointerNums  IntSlice
	targetSame   cellGroupType
	targetGroup  int
	//If description provided, the description MUST match.
	description string
	//If descriptions provided, ONE of the descriptions must match.
	//generally used in conjunction with solveTechniqueMatchModeAny.
	descriptions []string
	debugPrint   bool
}

func humanSolveTechniqueTestHelper(t *testing.T, puzzleName string, techniqueName string, options solveTechniqueTestHelperOptions) {
	//TODO: test for col and block as well
	grid := NewGrid()
	grid.LoadFromFile(puzzlePath(puzzleName))

	if options.transpose {
		grid = grid.transpose()
	}

	solver := techniquesByName[techniqueName]

	if solver == nil {
		t.Fatal("Couldn't find technique object: ", techniqueName)
	}

	steps := solver.Find(grid)

	if len(steps) == 0 {
		t.Fatal(techniqueName, " didn't find a cell it should have.")
	}

	step := steps[0]

	if options.debugPrint {
		log.Println(step)
	}

	if options.matchMode == solveTechniqueMatchModeAll {

		//All must match

		if options.targetCells != nil {
			if !step.TargetCells.sameAsRefs(options.targetCells) {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			if !step.PointerCells.sameAsRefs(options.pointerCells) {
				t.Error(techniqueName, " had the wrong pointer cells: ", step.PointerCells)
			}
		}

		switch options.targetSame {
		case GROUP_ROW:
			if !step.TargetCells.SameRow() || step.TargetCells.Row() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong row :", step.TargetCells.Row())
			}
		case GROUP_BLOCK:
			if !step.TargetCells.SameBlock() || step.TargetCells.Block() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong block :", step.TargetCells.Block())
			}
		case GROUP_COL:
			if !step.TargetCells.SameCol() || step.TargetCells.Col() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong col :", step.TargetCells.Col())
			}
		case GROUP_NONE:
			//Do nothing
		default:
			t.Error("human solve technique helper error: unsupported group type: ", options.targetSame)
		}

		if options.targetNums != nil {
			if !step.TargetNums.SameContentAs(options.targetNums) {
				t.Error(techniqueName, " found the wrong numbers: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			if !step.PointerNums.SameContentAs(options.pointerNums) {
				t.Error(techniqueName, "found the wrong numbers:", step.PointerNums)
			}
		}
	} else if options.matchMode == solveTechniqueMatchModeAny {

		foundMatch := false

		if options.targetCells != nil {
			foundMatch = false
			for _, ref := range options.targetCells {
				for _, cell := range step.TargetCells {
					if ref.Cell(grid) == cell {
						//TODO: break out early
						foundMatch = true
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			t.Error("Pointer cells in match mode any not yet supported.")
		}

		if options.targetSame != GROUP_NONE {
			t.Error("Target Same in match mode any not yet supported.")
		}

		if options.targetNums != nil {
			foundMatch = false
			for _, targetNum := range options.targetNums {
				for _, num := range step.TargetNums {
					if targetNum == num {
						foundMatch = true
						//TODO: break early here.
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target nums: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			foundMatch = false
			for _, pointerNum := range options.pointerNums {
				for _, num := range step.PointerNums {
					if pointerNum == num {
						foundMatch = true
						//TODO: break early here
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong pointer nums: ", step.PointerNums)
			}
		}
	}

	if options.description != "" {
		//Normalize the step so that the description will be stable for the test.
		step.normalize()
		description := solver.Description(step)
		if description != options.description {
			t.Error("Wrong description for ", techniqueName, ". Got:*", description, "* expected: *", options.description, "*")
		}
	} else if options.descriptions != nil {
		foundMatch := false
		step.normalize()
		description := solver.Description(step)
		for _, targetDescription := range options.descriptions {
			if description == targetDescription {
				foundMatch = true
			}
		}
		if !foundMatch {
			t.Error("No descriptions matched for ", techniqueName, ". Got:*", description)
		}
	}

	//TODO: we should do exhaustive testing of SolveStep application. We used to test it here, but as long as targetCells and targetNums are correct it should be fine.

	grid.Done()
}
<commit_msg>TESTS FAIL. Also did the load puzzle check in the main solve technique checker<commit_after>package sudoku

import (
	"log"
	"testing"
)

func TestSubsetIndexes(t *testing.T) {
	result := subsetIndexes(3, 1)
	expectedResult := [][]int{[]int{0}, []int{1}, []int{2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(3, 2)
	expectedResult = [][]int{[]int{0, 1}, []int{0, 2}, []int{1, 2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(5, 3)
	expectedResult = [][]int{[]int{0, 1, 2}, []int{0, 1, 3}, []int{0, 1, 4}, []int{0, 2, 3}, []int{0, 2, 4}, []int{0, 3, 4}, []int{1, 2, 3}, []int{1, 2, 4}, []int{1, 3, 4}, []int{2, 3, 4}}
	subsetIndexHelper(t, result, expectedResult)

	if subsetIndexes(1, 2) != nil {
		t.Log("Subset indexes returned a subset where the length is greater than the len")
		t.Fail()
	}

}

func subsetIndexHelper(t *testing.T, result [][]int, expectedResult [][]int) {
	if len(result) != len(expectedResult) {
		t.Log("subset indexes returned wrong number of results for: ", result, " :", expectedResult)
		t.FailNow()
	}
	for i, item := range result {
		if len(item) != len(expectedResult[0]) {
			t.Log("subset indexes returned a result with wrong numbrer of items ", i, " : ", result, " : ", expectedResult)
			t.FailNow()
		}
		for j, value := range item {
			if value != expectedResult[i][j] {
				t.Log("Subset indexes had wrong number at ", i, ",", j, " : ", result, " : ", expectedResult)
				t.Fail()
			}
		}
	}
}

type solveTechniqueMatchMode int

const (
	solveTechniqueMatchModeAll = iota
	solveTechniqueMatchModeAny
)

type solveTechniqueTestHelperOptions struct {
	transpose bool
	//Whether the descriptions of cells are a list of legal possible individual values, or must all match.
	matchMode    solveTechniqueMatchMode
	targetCells  []cellRef
	pointerCells []cellRef
	targetNums   IntSlice
	pointerNums  IntSlice
	targetSame   cellGroupType
	targetGroup  int
	//If description provided, the description MUST match.
	description string
	//If descriptions provided, ONE of the descriptions must match.
	//generally used in conjunction with solveTechniqueMatchModeAny.
	descriptions []string
	debugPrint   bool
}

func humanSolveTechniqueTestHelper(t *testing.T, puzzleName string, techniqueName string, options solveTechniqueTestHelperOptions) {
	//TODO: test for col and block as well
	grid := NewGrid()
	if !grid.LoadFromFile(puzzlePath(puzzleName)) {
		t.Fatal("Couldn't load puzzle ", puzzleName)
	}

	if options.transpose {
		grid = grid.transpose()
	}

	solver := techniquesByName[techniqueName]

	if solver == nil {
		t.Fatal("Couldn't find technique object: ", techniqueName)
	}

	steps := solver.Find(grid)

	if len(steps) == 0 {
		t.Fatal(techniqueName, " didn't find a cell it should have.")
	}

	step := steps[0]

	if options.debugPrint {
		log.Println(step)
	}

	if options.matchMode == solveTechniqueMatchModeAll {

		//All must match

		if options.targetCells != nil {
			if !step.TargetCells.sameAsRefs(options.targetCells) {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			if !step.PointerCells.sameAsRefs(options.pointerCells) {
				t.Error(techniqueName, " had the wrong pointer cells: ", step.PointerCells)
			}
		}

		switch options.targetSame {
		case GROUP_ROW:
			if !step.TargetCells.SameRow() || step.TargetCells.Row() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong row :", step.TargetCells.Row())
			}
		case GROUP_BLOCK:
			if !step.TargetCells.SameBlock() || step.TargetCells.Block() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong block :", step.TargetCells.Block())
			}
		case GROUP_COL:
			if !step.TargetCells.SameCol() || step.TargetCells.Col() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong col :", step.TargetCells.Col())
			}
		case GROUP_NONE:
			//Do nothing
		default:
			t.Error("human solve technique helper error: unsupported group type: ", options.targetSame)
		}

		if options.targetNums != nil {
			if !step.TargetNums.SameContentAs(options.targetNums) {
				t.Error(techniqueName, " found the wrong numbers: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			if !step.PointerNums.SameContentAs(options.pointerNums) {
				t.Error(techniqueName, "found the wrong numbers:", step.PointerNums)
			}
		}
	} else if options.matchMode == solveTechniqueMatchModeAny {

		foundMatch := false

		if options.targetCells != nil {
			foundMatch = false
			for _, ref := range options.targetCells {
				for _, cell := range step.TargetCells {
					if ref.Cell(grid) == cell {
						//TODO: break out early
						foundMatch = true
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			t.Error("Pointer cells in match mode any not yet supported.")
		}

		if options.targetSame != GROUP_NONE {
			t.Error("Target Same in match mode any not yet supported.")
		}

		if options.targetNums != nil {
			foundMatch = false
			for _, targetNum := range options.targetNums {
				for _, num := range step.TargetNums {
					if targetNum == num {
						foundMatch = true
						//TODO: break early here.
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target nums: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			foundMatch = false
			for _, pointerNum := range options.pointerNums {
				for _, num := range step.PointerNums {
					if pointerNum == num {
						foundMatch = true
						//TODO: break early here
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong pointer nums: ", step.PointerNums)
			}
		}
	}

	if options.description != "" {
		//Normalize the step so that the description will be stable for the test.
		step.normalize()
		description := solver.Description(step)
		if description != options.description {
			t.Error("Wrong description for ", techniqueName, ". Got:*", description, "* expected: *", options.description, "*")
		}
	} else if options.descriptions != nil {
		foundMatch := false
		step.normalize()
		description := solver.Description(step)
		for _, targetDescription := range options.descriptions {
			if description == targetDescription {
				foundMatch = true
			}
		}
		if !foundMatch {
			t.Error("No descriptions matched for ", techniqueName, ". Got:*", description)
		}
	}

	//TODO: we should do exhaustive testing of SolveStep application. We used to test it here, but as long as targetCells and targetNums are correct it should be fine.

	grid.Done()
}
<|endoftext|>


<commit_before>package sudoku

import (
	"fmt"
	"log"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be
	return self.difficultyHelper(200.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	//TODO: implement this
	return "ERROR: NOT IMPLEMENTED"
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.
	//TODO: Implement this.

	getter := grid.queue().DefaultGetter()

	_MAX_IMPLICATION_STEPS := 6

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)
		secondAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum,
			firstAccumulator)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum,
			secondAccumulator)

		//TODO:Check if the sets overlap.

		doPrint := candidateCell.Row() == 1 && candidateCell.Col() == 0

		//For these debugging purposes, only print out the candidateCell we know to be interesting in the test case.
		if doPrint {
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//accumulate forward, so the last generation has ALL cells affected in any generation on that branch
		firstAccumulator.accumulateGenerations()
		secondAccumulator.accumulateGenerations()

		foundOne := false

		for generation := _MAX_IMPLICATION_STEPS - 1; generation >= 0 && !foundOne; generation-- {

			if doPrint {
				log.Println(generation)
			}

			//Check for any overlap at the last generation
			firstAffectedCells := firstAccumulator[generation].filledNumbers
			secondAffectedCells := secondAccumulator[generation].filledNumbers

			for key, val := range firstAffectedCells {

				//Skip the candidateCell, because that's not a meaningful overlap--we set that one as a way of branching!
				if key == candidateCell.ref() {
					continue
				}

				if num, ok := secondAffectedCells[key]; ok {
					//Found cell overlap! ... is the forced number the same?
					if val == num {
						//Yup, seems like we've found a cell that is forced to the same value on either branch.
						step := &SolveStep{self,
							CellSlice{key.Cell(grid)},
							IntSlice{val},
							CellSlice{candidateCell},
							candidateCell.Possibilities(),
						}

						if doPrint {
							log.Println(step)
						}

						if step.IsUseful(grid) {
							foundOne = true
							select {
							case results <- step:
							case <-done:
								return
							}
						}
					}
				}
			}
		}

		//TODO: we should prefer solutions where the total implications on both branches are minimized.
		//For example, if only one implication is requried on left, but 4 are on right, that's preferable to one where
		//three implications are required on both sides.
		//TODO: do we really need the cellSet? (if we remove it, add a note to remove it from cellslice.go)
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

	}
}

type chainSearcherGenerationDetails struct {
	affectedCells cellSet
	filledNumbers map[cellRef]int
}

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map\n"
	for cell, num := range c.filledNumbers {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []*chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

//accumulateGenerations goes through each generation (youngest to newest)
//and squaches older generation maps into each generation, so each
//generation's map represents the totality of all cells seen at that point.
func (c chainSearcherAccumulator) accumulateGenerations() {
	for i := len(c) - 2; i >= 0; i-- {
		lastGeneration := c[i+1].filledNumbers
		currentGeneration := c[i].filledNumbers
		for key, val := range lastGeneration {
			currentGeneration[key] = val
		}
	}
}

func makeChainSeacherAccumulator(size int) chainSearcherAccumulator {
	result := make(chainSearcherAccumulator, size)
	for i := 0; i < size; i++ {
		result[i] = &chainSearcherGenerationDetails{
			affectedCells: make(cellSet),
			filledNumbers: make(map[cellRef]int),
		}
	}
	return result
}

func chainSearcher(i int, cell *Cell, numToApply int, accumulator chainSearcherAccumulator) {
	if i <= 0 || cell == nil {
		//Base case
		return
	}

	if i-1 >= len(accumulator) {
		panic("The accumulator provided was not big enough for the i provided.")
	}

	generationDetails := accumulator[i-1]

	//Find the nextCells that WILL have their numbers forced by the cell we're thinking of fillint.
	cellsToVisit := cell.Neighbors().FilterByPossible(numToApply).FilterByNumPossibilities(2)

	//Now that we know which cells will be affected and what their next number will be,
	//set the number in the given cell and then recurse downward down each branch.
	cell.SetNumber(numToApply)

	generationDetails.affectedCells[cell.ref()] = true
	generationDetails.filledNumbers[cell.ref()] = numToApply

	for _, cellToVisit := range cellsToVisit {

		possibilities := cellToVisit.Possibilities()

		if len(possibilities) != 1 {
			panic("Expected the cell to have one possibility")
		}

		forcedNum := possibilities[0]

		//Each branch modifies the grid, so create a new copy
		newGrid := cellToVisit.grid.Copy()
		cellToVisit = cellToVisit.InGrid(newGrid)

		//Recurse downward
		chainSearcher(i-1, cellToVisit, forcedNum, accumulator)

	}

}
<commit_msg>TESTS FAIL. Small tweak to which debugPrint statements we... print.<commit_after>package sudoku

import (
	"fmt"
	"log"
	"strconv"
)

type forcingChainsTechnique struct {
	*basicSolveTechnique
}

func (self *forcingChainsTechnique) HumanLikelihood() float64 {
	//TODO: figure out what the baseDifficulty should be
	return self.difficultyHelper(200.0)
}

func (self *forcingChainsTechnique) Description(step *SolveStep) string {
	//TODO: implement this
	return "ERROR: NOT IMPLEMENTED"
}

func (self *forcingChainsTechnique) Find(grid *Grid, results chan *SolveStep, done chan bool) {
	//TODO: test that this will find multiple if they exist.
	//TODO: Implement this.

	getter := grid.queue().DefaultGetter()

	_MAX_IMPLICATION_STEPS := 6

	for {

		//Check if it's time to stop.
		select {
		case <-done:
			return
		default:
		}

		candidate := getter.GetSmallerThan(3)

		if candidate == nil {
			break
		}

		candidateCell := candidate.(*Cell)

		if len(candidateCell.Possibilities()) != 2 {
			//We found one with 1 possibility, which isn't interesting for us--nakedSingle should do that one.
			continue
		}

		firstPossibilityNum := candidateCell.Possibilities()[0]
		secondPossibilityNum := candidateCell.Possibilities()[1]

		firstGrid := grid.Copy()
		secondGrid := grid.Copy()

		//Check that the neighbor isn't just already having a single possibility, because then this technique is overkill.

		firstAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)
		secondAccumulator := makeChainSeacherAccumulator(_MAX_IMPLICATION_STEPS)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(firstGrid),
			firstPossibilityNum,
			firstAccumulator)

		chainSearcher(_MAX_IMPLICATION_STEPS,
			candidateCell.InGrid(secondGrid),
			secondPossibilityNum,
			secondAccumulator)

		//TODO:Check if the sets overlap.

		doPrint := candidateCell.Row() == 1 && candidateCell.Col() == 0

		//For these debugging purposes, only print out the candidateCell we know to be interesting in the test case.
		if doPrint {
			log.Println(firstAccumulator)
			log.Println(secondAccumulator)
		}

		//See if either branch, at some generation, has the same cell forced to the same number in either generation.

		//accumulate forward, so the last generation has ALL cells affected in any generation on that branch
		firstAccumulator.accumulateGenerations()
		secondAccumulator.accumulateGenerations()

		foundOne := false

		for generation := _MAX_IMPLICATION_STEPS - 1; generation >= 0 && !foundOne; generation-- {

			//Check for any overlap at the last generation
			firstAffectedCells := firstAccumulator[generation].filledNumbers
			secondAffectedCells := secondAccumulator[generation].filledNumbers

			for key, val := range firstAffectedCells {

				//Skip the candidateCell, because that's not a meaningful overlap--we set that one as a way of branching!
				if key == candidateCell.ref() {
					continue
				}

				if num, ok := secondAffectedCells[key]; ok {
					//Found cell overlap! ... is the forced number the same?
					if val == num {
						//Yup, seems like we've found a cell that is forced to the same value on either branch.
						step := &SolveStep{self,
							CellSlice{key.Cell(grid)},
							IntSlice{val},
							CellSlice{candidateCell},
							candidateCell.Possibilities(),
						}

						if doPrint {
							log.Println(step)
							log.Println("Candidate Cell", candidateCell.ref())
						}

						if step.IsUseful(grid) {
							foundOne = true
							if doPrint {
								log.Println("Found solution on generation: ", generation)
							}
							select {
							case results <- step:
							case <-done:
								return
							}
						}
					}
				}
			}
		}

		//TODO: we should prefer solutions where the total implications on both branches are minimized.
		//For example, if only one implication is requried on left, but 4 are on right, that's preferable to one where
		//three implications are required on both sides.
		//TODO: do we really need the cellSet? (if we remove it, add a note to remove it from cellslice.go)
		//TODO: figure out a way to only compute a generation if required on each branch (don't compute all the way to _MAX_IMPLICATIONS to start)

	}
}

type chainSearcherGenerationDetails struct {
	affectedCells cellSet
	filledNumbers map[cellRef]int
}

func (c chainSearcherGenerationDetails) String() string {
	result := "Begin map\n"
	for cell, num := range c.filledNumbers {
		result += "\t" + cell.String() + " : " + strconv.Itoa(num) + "\n"
	}
	result += "End map\n"
	return result
}

type chainSearcherAccumulator []*chainSearcherGenerationDetails

func (c chainSearcherAccumulator) String() string {
	result := "Accumulator[\n"
	for _, rec := range c {
		result += fmt.Sprintf("%s\n", rec)
	}
	result += "]\n"
	return result
}

//accumulateGenerations goes through each generation (youngest to newest)
//and squaches older generation maps into each generation, so each
//generation's map represents the totality of all cells seen at that point.
func (c chainSearcherAccumulator) accumulateGenerations() {
	for i := len(c) - 2; i >= 0; i-- {
		lastGeneration := c[i+1].filledNumbers
		currentGeneration := c[i].filledNumbers
		for key, val := range lastGeneration {
			currentGeneration[key] = val
		}
	}
}

func makeChainSeacherAccumulator(size int) chainSearcherAccumulator {
	result := make(chainSearcherAccumulator, size)
	for i := 0; i < size; i++ {
		result[i] = &chainSearcherGenerationDetails{
			affectedCells: make(cellSet),
			filledNumbers: make(map[cellRef]int),
		}
	}
	return result
}

func chainSearcher(i int, cell *Cell, numToApply int, accumulator chainSearcherAccumulator) {
	if i <= 0 || cell == nil {
		//Base case
		return
	}

	if i-1 >= len(accumulator) {
		panic("The accumulator provided was not big enough for the i provided.")
	}

	generationDetails := accumulator[i-1]

	//Find the nextCells that WILL have their numbers forced by the cell we're thinking of fillint.
	cellsToVisit := cell.Neighbors().FilterByPossible(numToApply).FilterByNumPossibilities(2)

	//Now that we know which cells will be affected and what their next number will be,
	//set the number in the given cell and then recurse downward down each branch.
	cell.SetNumber(numToApply)

	generationDetails.affectedCells[cell.ref()] = true
	generationDetails.filledNumbers[cell.ref()] = numToApply

	for _, cellToVisit := range cellsToVisit {

		possibilities := cellToVisit.Possibilities()

		if len(possibilities) != 1 {
			panic("Expected the cell to have one possibility")
		}

		forcedNum := possibilities[0]

		//Each branch modifies the grid, so create a new copy
		newGrid := cellToVisit.grid.Copy()
		cellToVisit = cellToVisit.InGrid(newGrid)

		//Recurse downward
		chainSearcher(i-1, cellToVisit, forcedNum, accumulator)

	}

}
<|endoftext|>


<commit_before>package sudoku

import (
	"testing"
)

//TODO: test a few more puzzles to make sure I'm exercising it correctly.

func swordfishExampleGrid(t *testing.T) *Grid {
	grid := NewGrid()

	puzzleName := "swordfish_example.sdk"

	if !grid.LoadFromFile(puzzlePath(puzzleName)) {
		t.Fatal("Couldn't load puzzle ", puzzleName)
	}

	//Set up the grid correctly for the Swordfish technique to work. The
	//example we use is a grid that has other work done to exclude
	//possibilities from certain cells.

	//TODO: it's a smell that there's no way to serialize and load up a grid
	//with extra excludes set.
	excludedConfig := map[cellRef]IntSlice{
		cellRef{0, 0}: IntSlice{1, 8},
		cellRef{1, 3}: IntSlice{1},
		cellRef{1, 4}: IntSlice{1, 8},
		cellRef{2, 3}: IntSlice{1},
		cellRef{2, 5}: IntSlice{1, 8},
		cellRef{3, 0}: IntSlice{2, 8},
		cellRef{4, 0}: IntSlice{7},
		cellRef{4, 1}: IntSlice{7},
		cellRef{7, 3}: IntSlice{1, 6},
		cellRef{7, 5}: IntSlice{1},
	}

	for ref, ints := range excludedConfig {
		cell := ref.Cell(grid)
		for _, exclude := range ints {
			cell.SetExcluded(exclude, true)
		}
	}

	return grid
}

func TestSwordfishCol(t *testing.T) {

	techniqueVariantsTestHelper(t, "Swordfish Col")

	grid := swordfishExampleGrid(t)

	options := solveTechniqueTestHelperOptions{
		targetCells:  []cellRef{{1, 1}, {5, 4}},
		pointerCells: []cellRef{{1, 0}, {1, 5}, {5, 3}, {5, 5}, {8, 0}, {8, 3}},
		targetNums:   IntSlice{1},
		//TODO: test description
	}
	options.stepsToCheck.grid = grid

	//TODO: it's not possible to just pass in an override grid to humanSolveTechniqueTestHelper as
	//is, because we're overloading passing it to stepsToCheck. That's a smell.
	grid, solver, steps := humanSolveTechniqueTestHelperStepGenerator(t, "NOOP", "Swordfish Col", options)

	options.stepsToCheck.grid = grid
	options.stepsToCheck.solver = solver
	options.stepsToCheck.steps = steps

	humanSolveTechniqueTestHelper(t, "NOOP", "Swordfish Col", options)

}

func TestSwordfishRow(t *testing.T) {

	techniqueVariantsTestHelper(t, "Swordfish Row")

	grid := swordfishExampleGrid(t)
	grid = grid.transpose()

	options := solveTechniqueTestHelperOptions{
		targetCells:  []cellRef{{1, 1}, {4, 5}},
		pointerCells: []cellRef{{0, 1}, {5, 1}, {3, 5}, {5, 5}, {0, 8}, {3, 8}},
		targetNums:   IntSlice{1},
		//TODO: test description
	}
	options.stepsToCheck.grid = grid

	//TODO: it's not possible to just pass in an override grid to humanSolveTechniqueTestHelper as
	//is, because we're overloading passing it to stepsToCheck. That's a smell.
	grid, solver, steps := humanSolveTechniqueTestHelperStepGenerator(t, "NOOP", "Swordfish Row", options)

	options.stepsToCheck.grid = grid
	options.stepsToCheck.solver = solver
	options.stepsToCheck.steps = steps

	humanSolveTechniqueTestHelper(t, "NOOP", "Swordfish Row", options)

}
<commit_msg>Added tests for swordfish.Description<commit_after>package sudoku

import (
	"testing"
)

//TODO: test a few more puzzles to make sure I'm exercising it correctly.

func swordfishExampleGrid(t *testing.T) *Grid {
	grid := NewGrid()

	puzzleName := "swordfish_example.sdk"

	if !grid.LoadFromFile(puzzlePath(puzzleName)) {
		t.Fatal("Couldn't load puzzle ", puzzleName)
	}

	//Set up the grid correctly for the Swordfish technique to work. The
	//example we use is a grid that has other work done to exclude
	//possibilities from certain cells.

	//TODO: it's a smell that there's no way to serialize and load up a grid
	//with extra excludes set.
	excludedConfig := map[cellRef]IntSlice{
		cellRef{0, 0}: IntSlice{1, 8},
		cellRef{1, 3}: IntSlice{1},
		cellRef{1, 4}: IntSlice{1, 8},
		cellRef{2, 3}: IntSlice{1},
		cellRef{2, 5}: IntSlice{1, 8},
		cellRef{3, 0}: IntSlice{2, 8},
		cellRef{4, 0}: IntSlice{7},
		cellRef{4, 1}: IntSlice{7},
		cellRef{7, 3}: IntSlice{1, 6},
		cellRef{7, 5}: IntSlice{1},
	}

	for ref, ints := range excludedConfig {
		cell := ref.Cell(grid)
		for _, exclude := range ints {
			cell.SetExcluded(exclude, true)
		}
	}

	return grid
}

func TestSwordfishCol(t *testing.T) {

	techniqueVariantsTestHelper(t, "Swordfish Col")

	grid := swordfishExampleGrid(t)

	options := solveTechniqueTestHelperOptions{
		targetCells:  []cellRef{{1, 1}, {5, 4}},
		pointerCells: []cellRef{{1, 0}, {1, 5}, {5, 3}, {5, 5}, {8, 0}, {8, 3}},
		targetNums:   IntSlice{1},
		description:  "1 is only possible in two cells each in three different columns, all of which align onto three rows, which means that 1 can't be in any of the other cells in those rows ((1,1) and (5,4))",
	}
	options.stepsToCheck.grid = grid

	//TODO: it's not possible to just pass in an override grid to humanSolveTechniqueTestHelper as
	//is, because we're overloading passing it to stepsToCheck. That's a smell.
	grid, solver, steps := humanSolveTechniqueTestHelperStepGenerator(t, "NOOP", "Swordfish Col", options)

	options.stepsToCheck.grid = grid
	options.stepsToCheck.solver = solver
	options.stepsToCheck.steps = steps

	humanSolveTechniqueTestHelper(t, "NOOP", "Swordfish Col", options)

}

func TestSwordfishRow(t *testing.T) {

	techniqueVariantsTestHelper(t, "Swordfish Row")

	grid := swordfishExampleGrid(t)
	grid = grid.transpose()

	options := solveTechniqueTestHelperOptions{
		targetCells:  []cellRef{{1, 1}, {4, 5}},
		pointerCells: []cellRef{{0, 1}, {5, 1}, {3, 5}, {5, 5}, {0, 8}, {3, 8}},
		targetNums:   IntSlice{1},
		description:  "1 is only possible in two cells each in three different rows, all of which align onto three columns, which means that 1 can't be in any of the other cells in those columns ((1,1) and (4,5))",
	}
	options.stepsToCheck.grid = grid

	//TODO: it's not possible to just pass in an override grid to humanSolveTechniqueTestHelper as
	//is, because we're overloading passing it to stepsToCheck. That's a smell.
	grid, solver, steps := humanSolveTechniqueTestHelperStepGenerator(t, "NOOP", "Swordfish Row", options)

	options.stepsToCheck.grid = grid
	options.stepsToCheck.solver = solver
	options.stepsToCheck.steps = steps

	humanSolveTechniqueTestHelper(t, "NOOP", "Swordfish Row", options)

}
<|endoftext|>


<commit_before>package crane

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/alecthomas/kingpin"
)

var cfg Config
var allowed []string

var (
	app         = kingpin.New("crane", "Lift containers with ease").Interspersed(false).DefaultEnvars()
	verboseFlag = app.Flag("verbose", "Enable verbose output.").Short('v').Bool()
	dryRunFlag  = app.Flag("dry-run", "Dry run (implicit verbose, no side effects).").Bool()
	configFlag  = app.Flag(
		"config",
		"Location of config file.",
	).Short('c').PlaceHolder("~/crane.yaml").String()
	prefixFlag = app.Flag(
		"prefix",
		"Container prefix.",
	).Short('p').String()
	excludeFlag = app.Flag(
		"exclude",
		"Exclude group or container. Can be repeated.",
	).Short('e').PlaceHolder("container|group").Strings()
	onlyFlag = app.Flag(
		"only",
		"Include only group or container.",
	).Short('o').PlaceHolder("container|group").String()
	tagFlag = app.Flag(
		"tag",
		"Override image tags.",
	).OverrideDefaultFromEnvar("CRANE_TAG").String()

	liftCommand = app.Command(
		"lift",
		"Build or pull images if they don't exist, then run or start the containers.",
	)
	liftNoCacheFlag = liftCommand.Flag(
		"no-cache",
		"Build the image without any cache.",
	).Short('n').Bool()
	liftParallelFlag = liftCommand.Flag(
		"parallel",
		"Define how many containers are provisioned in parallel.",
	).Short('l').Default("1").Int()
	liftTargetArg = liftCommand.Arg("target", "Target of command").String()
	liftCmdArg    = liftCommand.Arg("cmd", "Command for container").Strings()

	versionCommand = app.Command(
		"version",
		"Displays the version of Crane.",
	)

	statsCommand = app.Command(
		"stats",
		"Displays statistics about containers.",
	)
	statsNoStreamFlag = statsCommand.Flag(
		"no-stream",
		"Disable stats streaming (Docker >= 1.7).",
	).Short('n').Bool()
	statsTargetArg = statsCommand.Arg("target", "Target of command").String()

	statusCommand = app.Command(
		"status",
		"Displays status of containers.",
	)
	noTruncFlag = liftCommand.Flag(
		"no-trunc",
		"Don't truncate output.",
	).Bool()
	statusTargetArg = statusCommand.Arg("target", "Target of command").String()

	pushCommand = app.Command(
		"push",
		"Push the containers to the registry.",
	)
	pushTargetArg = pushCommand.Arg("target", "Target of command").String()

	pauseCommand = app.Command(
		"pause",
		"Pause the containers.",
	)
	pauseTargetArg = pauseCommand.Arg("target", "Target of command").String()

	unpauseCommand = app.Command(
		"unpause",
		"Unpause the containers.",
	)
	unpauseTargetArg = unpauseCommand.Arg("target", "Target of command").String()

	startCommand = app.Command(
		"start",
		"Start the containers.",
	)
	startTargetArg = startCommand.Arg("target", "Target of command").String()

	stopCommand = app.Command(
		"stop",
		"Stop the containers.",
	)
	stopTargetArg = stopCommand.Arg("target", "Target of command").String()

	killCommand = app.Command(
		"kill",
		"Kill the containers.",
	)
	killTargetArg = killCommand.Arg("target", "Target of command").String()

	execCommand = app.Command(
		"exec",
		"Execute command in the container(s).",
	)
	execTargetArg = execCommand.Arg("target", "Target of command").String()
	execCmdArg    = execCommand.Arg("cmd", "Command for container").Strings()

	rmCommand = app.Command(
		"rm",
		"Remove the containers.",
	)
	forceRmFlag = rmCommand.Flag(
		"force",
		"Kill containers if they are running first.",
	).Short('f').Bool()
	rmTargetArg = rmCommand.Arg("target", "Target of command").String()

	runCommand = app.Command(
		"run",
		"Run the containers.",
	)
	runTargetArg = runCommand.Arg("target", "Target of command").String()
	runCmdArg    = runCommand.Arg("cmd", "Command for container").Strings()

	createCommand = app.Command(
		"create",
		"Create the containers.",
	)
	createTargetArg = createCommand.Arg("target", "Target of command").String()
	createCmdArg    = createCommand.Arg("cmd", "Command for container").Strings()

	provisionCommand = app.Command(
		"provision",
		"Build or pull images.",
	)
	provisionNoCacheFlag = provisionCommand.Flag(
		"no-cache",
		"Build the image without any cache.",
	).Short('n').Bool()
	provisionParallelFlag = provisionCommand.Flag(
		"parallel",
		"Define how many containers are provisioned in parallel.",
	).Short('l').Default("1").Int()
	provisionTargetArg = provisionCommand.Arg("target", "Target of command").String()

	pullCommand = app.Command(
		"pull",
		"Pull images.",
	)
	pullTargetArg = pullCommand.Arg("target", "Target of command").String()

	logsCommand = app.Command(
		"logs",
		"Display container logs.",
	)
	followFlag = logsCommand.Flag(
		"follow",
		"Follow log output.",
	).Short('f').Bool()
	tailFlag = logsCommand.Flag(
		"tail",
		"Output the specified number of lines at the end of logs.",
	).String()
	timestampsFlag = logsCommand.Flag(
		"timestamps",
		"Show timestamps.",
	).Short('t').Bool()
	colorizeFlag = logsCommand.Flag(
		"colorize",
		"Output the lines with one color per container.",
	).Short('z').Bool()
	sinceFlag = logsCommand.Flag(
		"since",
		"Show logs since timestamp (Docker >= 1.7).",
	).String()
	logsTargetArg = logsCommand.Arg("target", "Target of command").String()

	generateCommand = app.Command(
		"generate",
		"Generate files by passing the config to a given template.",
	)
	templateFlag = generateCommand.Flag(
		"template",
		"Template to use.",
	).Short('t').String()
	outputFlag = generateCommand.Flag(
		"output",
		"The file(s) to write the output to.",
	).Short('O').String()
	generateTargetArg = generateCommand.Arg("target", "Target of command").String()

	syncCommand = app.Command(
		"mac-sync",
		"Docker for Mac sync",
	)
	syncStartCommand = syncCommand.Command(
		"start",
		"Start Docker for Mac sync",
	)
	syncStartVolumeArg = syncStartCommand.Arg("volume", "Folders to sync").String()
	syncStopCommand    = syncCommand.Command(
		"stop",
		"Stop Docker for Mac sync",
	)
	syncStopVolumeArg = syncStopCommand.Arg("volume", "Folders to sync").String()
	syncStatusCommand = syncCommand.Command(
		"status",
		"Status of Docker for Mac syncs",
	)
)

func isVerbose() bool {
	return *verboseFlag || *dryRunFlag
}

func isDryRun() bool {
	return *dryRunFlag
}

func commandAction(targetFlag string, wrapped func(unitOfWork *UnitOfWork), mightStartRelated bool) {

	cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
	allowed = allowedContainers(*excludeFlag, *onlyFlag)
	dependencyMap := cfg.DependencyMap()
	target, err := NewTarget(dependencyMap, targetFlag)
	if err != nil {
		panic(StatusError{err, 78})
	}
	unitOfWork, err := NewUnitOfWork(dependencyMap, target.all())
	if err != nil {
		panic(StatusError{err, 78})
	}

	if isVerbose() {
		printInfof("Command will be applied to: %s", strings.Join(unitOfWork.targeted, ", "))
		if mightStartRelated {
			associated := unitOfWork.Associated()
			if len(associated) > 0 {
				printInfof("\nIf needed, also starts containers: %s", strings.Join(associated, ", "))
			}
			requiredNetworks := unitOfWork.RequiredNetworks()
			if len(requiredNetworks) > 0 {
				printInfof("\nIf needed, also creates networks: %s", strings.Join(requiredNetworks, ", "))
			}
			requiredVolumes := unitOfWork.RequiredVolumes()
			if len(requiredVolumes) > 0 {
				printInfof("\nIf needed, also creates volumes: %s", strings.Join(requiredVolumes, ", "))
			}
		}
		fmt.Printf("\n\n")
	}
	wrapped(unitOfWork)
}

func allowedContainers(excludedReference []string, onlyReference string) (containers []string) {
	allContainers := []string{}
	if len(onlyReference) == 0 {
		for name := range cfg.ContainerMap() {
			allContainers = append(allContainers, name)
		}
	} else {
		allContainers = cfg.ContainersForReference(onlyReference)
	}
	excludedContainers := []string{}
	for _, reference := range excludedReference {
		excludedContainers = append(excludedContainers, cfg.ContainersForReference(reference)...)
	}
	for _, name := range allContainers {
		if !includes(excludedContainers, name) {
			containers = append(containers, name)
		}
	}
	return
}

func runCli() {
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {

	case liftCommand.FullCommand():
		commandAction(*liftTargetArg, func(uow *UnitOfWork) {
			uow.Lift(*liftCmdArg, *liftNoCacheFlag, *liftParallelFlag)
		}, true)

	case versionCommand.FullCommand():
		fmt.Println("v2.10.2")

	case statsCommand.FullCommand():
		commandAction(*statsTargetArg, func(uow *UnitOfWork) {
			uow.Stats(*statsNoStreamFlag)
		}, false)

	case statusCommand.FullCommand():
		commandAction(*statusTargetArg, func(uow *UnitOfWork) {
			uow.Status(*noTruncFlag)
		}, false)

	case pushCommand.FullCommand():
		commandAction(*pushTargetArg, func(uow *UnitOfWork) {
			uow.Push()
		}, false)

	case unpauseCommand.FullCommand():
		commandAction(*unpauseTargetArg, func(uow *UnitOfWork) {
			uow.Unpause()
		}, false)

	case pauseCommand.FullCommand():
		commandAction(*pauseTargetArg, func(uow *UnitOfWork) {
			uow.Pause()
		}, false)

	case startCommand.FullCommand():
		commandAction(*startTargetArg, func(uow *UnitOfWork) {
			uow.Start()
		}, true)

	case stopCommand.FullCommand():
		commandAction(*stopTargetArg, func(uow *UnitOfWork) {
			uow.Stop()
		}, false)

	case killCommand.FullCommand():
		commandAction(*killTargetArg, func(uow *UnitOfWork) {
			uow.Kill()
		}, false)

	case execCommand.FullCommand():
		commandAction(*execTargetArg, func(uow *UnitOfWork) {
			uow.Exec(*execCmdArg)
		}, false)

	case rmCommand.FullCommand():
		commandAction(*rmTargetArg, func(uow *UnitOfWork) {
			uow.Rm(*forceRmFlag)
		}, false)

	case runCommand.FullCommand():
		commandAction(*runTargetArg, func(uow *UnitOfWork) {
			uow.Run(*runCmdArg)
		}, true)

	case createCommand.FullCommand():
		commandAction(*createTargetArg, func(uow *UnitOfWork) {
			uow.Create(*createCmdArg)
		}, true)

	case provisionCommand.FullCommand():
		commandAction(*provisionTargetArg, func(uow *UnitOfWork) {
			uow.Provision(*provisionNoCacheFlag, *provisionParallelFlag)
		}, false)

	case pullCommand.FullCommand():
		commandAction(*pullTargetArg, func(uow *UnitOfWork) {
			uow.PullImage()
		}, false)

	case logsCommand.FullCommand():
		commandAction(*logsTargetArg, func(uow *UnitOfWork) {
			uow.Logs(*followFlag, *timestampsFlag, *tailFlag, *colorizeFlag, *sinceFlag)
		}, false)

	case generateCommand.FullCommand():
		if len(*templateFlag) == 0 {
			printErrorf("ERROR: No template specified. The flag `--template` is required.\n")
			return
		}
		commandAction(*generateTargetArg, func(uow *UnitOfWork) {
			uow.Generate(*templateFlag, *outputFlag)
		}, false)

	case syncStartCommand.FullCommand():
		cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
		sync := cfg.MacSync(*syncStartVolumeArg)
		if sync == nil {
			printErrorf("ERROR: No such sync configured: %s.", *syncStartVolumeArg)
			return
		}
		sync.Start()

	case syncStopCommand.FullCommand():
		cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
		sync := cfg.MacSync(*syncStopVolumeArg)
		if sync == nil {
			printErrorf("ERROR: No such sync configured: %s.", *syncStartVolumeArg)
			return
		}
		sync.Stop()

	case syncStatusCommand.FullCommand():
		cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 1, '\t', 0)
		fmt.Fprintln(w, "VOLUME\tCONTAINER\tSTATUS")
		for _, name := range cfg.MacSyncNames() {
			s := cfg.MacSync(name)
			fmt.Fprintf(w, "%s\n", s.Volume()+"\t"+s.ContainerName()+"\t"+s.Status())
		}
		w.Flush()
	}
}
<commit_msg>Don't write container name if container does not exist yet<commit_after>package crane

import (
	"fmt"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/alecthomas/kingpin"
)

var cfg Config
var allowed []string

var (
	app         = kingpin.New("crane", "Lift containers with ease").Interspersed(false).DefaultEnvars()
	verboseFlag = app.Flag("verbose", "Enable verbose output.").Short('v').Bool()
	dryRunFlag  = app.Flag("dry-run", "Dry run (implicit verbose, no side effects).").Bool()
	configFlag  = app.Flag(
		"config",
		"Location of config file.",
	).Short('c').PlaceHolder("~/crane.yaml").String()
	prefixFlag = app.Flag(
		"prefix",
		"Container prefix.",
	).Short('p').String()
	excludeFlag = app.Flag(
		"exclude",
		"Exclude group or container. Can be repeated.",
	).Short('e').PlaceHolder("container|group").Strings()
	onlyFlag = app.Flag(
		"only",
		"Include only group or container.",
	).Short('o').PlaceHolder("container|group").String()
	tagFlag = app.Flag(
		"tag",
		"Override image tags.",
	).OverrideDefaultFromEnvar("CRANE_TAG").String()

	liftCommand = app.Command(
		"lift",
		"Build or pull images if they don't exist, then run or start the containers.",
	)
	liftNoCacheFlag = liftCommand.Flag(
		"no-cache",
		"Build the image without any cache.",
	).Short('n').Bool()
	liftParallelFlag = liftCommand.Flag(
		"parallel",
		"Define how many containers are provisioned in parallel.",
	).Short('l').Default("1").Int()
	liftTargetArg = liftCommand.Arg("target", "Target of command").String()
	liftCmdArg    = liftCommand.Arg("cmd", "Command for container").Strings()

	versionCommand = app.Command(
		"version",
		"Displays the version of Crane.",
	)

	statsCommand = app.Command(
		"stats",
		"Displays statistics about containers.",
	)
	statsNoStreamFlag = statsCommand.Flag(
		"no-stream",
		"Disable stats streaming (Docker >= 1.7).",
	).Short('n').Bool()
	statsTargetArg = statsCommand.Arg("target", "Target of command").String()

	statusCommand = app.Command(
		"status",
		"Displays status of containers.",
	)
	noTruncFlag = liftCommand.Flag(
		"no-trunc",
		"Don't truncate output.",
	).Bool()
	statusTargetArg = statusCommand.Arg("target", "Target of command").String()

	pushCommand = app.Command(
		"push",
		"Push the containers to the registry.",
	)
	pushTargetArg = pushCommand.Arg("target", "Target of command").String()

	pauseCommand = app.Command(
		"pause",
		"Pause the containers.",
	)
	pauseTargetArg = pauseCommand.Arg("target", "Target of command").String()

	unpauseCommand = app.Command(
		"unpause",
		"Unpause the containers.",
	)
	unpauseTargetArg = unpauseCommand.Arg("target", "Target of command").String()

	startCommand = app.Command(
		"start",
		"Start the containers.",
	)
	startTargetArg = startCommand.Arg("target", "Target of command").String()

	stopCommand = app.Command(
		"stop",
		"Stop the containers.",
	)
	stopTargetArg = stopCommand.Arg("target", "Target of command").String()

	killCommand = app.Command(
		"kill",
		"Kill the containers.",
	)
	killTargetArg = killCommand.Arg("target", "Target of command").String()

	execCommand = app.Command(
		"exec",
		"Execute command in the container(s).",
	)
	execTargetArg = execCommand.Arg("target", "Target of command").String()
	execCmdArg    = execCommand.Arg("cmd", "Command for container").Strings()

	rmCommand = app.Command(
		"rm",
		"Remove the containers.",
	)
	forceRmFlag = rmCommand.Flag(
		"force",
		"Kill containers if they are running first.",
	).Short('f').Bool()
	rmTargetArg = rmCommand.Arg("target", "Target of command").String()

	runCommand = app.Command(
		"run",
		"Run the containers.",
	)
	runTargetArg = runCommand.Arg("target", "Target of command").String()
	runCmdArg    = runCommand.Arg("cmd", "Command for container").Strings()

	createCommand = app.Command(
		"create",
		"Create the containers.",
	)
	createTargetArg = createCommand.Arg("target", "Target of command").String()
	createCmdArg    = createCommand.Arg("cmd", "Command for container").Strings()

	provisionCommand = app.Command(
		"provision",
		"Build or pull images.",
	)
	provisionNoCacheFlag = provisionCommand.Flag(
		"no-cache",
		"Build the image without any cache.",
	).Short('n').Bool()
	provisionParallelFlag = provisionCommand.Flag(
		"parallel",
		"Define how many containers are provisioned in parallel.",
	).Short('l').Default("1").Int()
	provisionTargetArg = provisionCommand.Arg("target", "Target of command").String()

	pullCommand = app.Command(
		"pull",
		"Pull images.",
	)
	pullTargetArg = pullCommand.Arg("target", "Target of command").String()

	logsCommand = app.Command(
		"logs",
		"Display container logs.",
	)
	followFlag = logsCommand.Flag(
		"follow",
		"Follow log output.",
	).Short('f').Bool()
	tailFlag = logsCommand.Flag(
		"tail",
		"Output the specified number of lines at the end of logs.",
	).String()
	timestampsFlag = logsCommand.Flag(
		"timestamps",
		"Show timestamps.",
	).Short('t').Bool()
	colorizeFlag = logsCommand.Flag(
		"colorize",
		"Output the lines with one color per container.",
	).Short('z').Bool()
	sinceFlag = logsCommand.Flag(
		"since",
		"Show logs since timestamp (Docker >= 1.7).",
	).String()
	logsTargetArg = logsCommand.Arg("target", "Target of command").String()

	generateCommand = app.Command(
		"generate",
		"Generate files by passing the config to a given template.",
	)
	templateFlag = generateCommand.Flag(
		"template",
		"Template to use.",
	).Short('t').String()
	outputFlag = generateCommand.Flag(
		"output",
		"The file(s) to write the output to.",
	).Short('O').String()
	generateTargetArg = generateCommand.Arg("target", "Target of command").String()

	syncCommand = app.Command(
		"mac-sync",
		"Docker for Mac sync",
	)
	syncStartCommand = syncCommand.Command(
		"start",
		"Start Docker for Mac sync",
	)
	syncStartVolumeArg = syncStartCommand.Arg("volume", "Folders to sync").String()
	syncStopCommand    = syncCommand.Command(
		"stop",
		"Stop Docker for Mac sync",
	)
	syncStopVolumeArg = syncStopCommand.Arg("volume", "Folders to sync").String()
	syncStatusCommand = syncCommand.Command(
		"status",
		"Status of Docker for Mac syncs",
	)
)

func isVerbose() bool {
	return *verboseFlag || *dryRunFlag
}

func isDryRun() bool {
	return *dryRunFlag
}

func commandAction(targetFlag string, wrapped func(unitOfWork *UnitOfWork), mightStartRelated bool) {

	cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
	allowed = allowedContainers(*excludeFlag, *onlyFlag)
	dependencyMap := cfg.DependencyMap()
	target, err := NewTarget(dependencyMap, targetFlag)
	if err != nil {
		panic(StatusError{err, 78})
	}
	unitOfWork, err := NewUnitOfWork(dependencyMap, target.all())
	if err != nil {
		panic(StatusError{err, 78})
	}

	if isVerbose() {
		printInfof("Command will be applied to: %s", strings.Join(unitOfWork.targeted, ", "))
		if mightStartRelated {
			associated := unitOfWork.Associated()
			if len(associated) > 0 {
				printInfof("\nIf needed, also starts containers: %s", strings.Join(associated, ", "))
			}
			requiredNetworks := unitOfWork.RequiredNetworks()
			if len(requiredNetworks) > 0 {
				printInfof("\nIf needed, also creates networks: %s", strings.Join(requiredNetworks, ", "))
			}
			requiredVolumes := unitOfWork.RequiredVolumes()
			if len(requiredVolumes) > 0 {
				printInfof("\nIf needed, also creates volumes: %s", strings.Join(requiredVolumes, ", "))
			}
		}
		fmt.Printf("\n\n")
	}
	wrapped(unitOfWork)
}

func allowedContainers(excludedReference []string, onlyReference string) (containers []string) {
	allContainers := []string{}
	if len(onlyReference) == 0 {
		for name := range cfg.ContainerMap() {
			allContainers = append(allContainers, name)
		}
	} else {
		allContainers = cfg.ContainersForReference(onlyReference)
	}
	excludedContainers := []string{}
	for _, reference := range excludedReference {
		excludedContainers = append(excludedContainers, cfg.ContainersForReference(reference)...)
	}
	for _, name := range allContainers {
		if !includes(excludedContainers, name) {
			containers = append(containers, name)
		}
	}
	return
}

func runCli() {
	switch kingpin.MustParse(app.Parse(os.Args[1:])) {

	case liftCommand.FullCommand():
		commandAction(*liftTargetArg, func(uow *UnitOfWork) {
			uow.Lift(*liftCmdArg, *liftNoCacheFlag, *liftParallelFlag)
		}, true)

	case versionCommand.FullCommand():
		fmt.Println("v2.10.2")

	case statsCommand.FullCommand():
		commandAction(*statsTargetArg, func(uow *UnitOfWork) {
			uow.Stats(*statsNoStreamFlag)
		}, false)

	case statusCommand.FullCommand():
		commandAction(*statusTargetArg, func(uow *UnitOfWork) {
			uow.Status(*noTruncFlag)
		}, false)

	case pushCommand.FullCommand():
		commandAction(*pushTargetArg, func(uow *UnitOfWork) {
			uow.Push()
		}, false)

	case unpauseCommand.FullCommand():
		commandAction(*unpauseTargetArg, func(uow *UnitOfWork) {
			uow.Unpause()
		}, false)

	case pauseCommand.FullCommand():
		commandAction(*pauseTargetArg, func(uow *UnitOfWork) {
			uow.Pause()
		}, false)

	case startCommand.FullCommand():
		commandAction(*startTargetArg, func(uow *UnitOfWork) {
			uow.Start()
		}, true)

	case stopCommand.FullCommand():
		commandAction(*stopTargetArg, func(uow *UnitOfWork) {
			uow.Stop()
		}, false)

	case killCommand.FullCommand():
		commandAction(*killTargetArg, func(uow *UnitOfWork) {
			uow.Kill()
		}, false)

	case execCommand.FullCommand():
		commandAction(*execTargetArg, func(uow *UnitOfWork) {
			uow.Exec(*execCmdArg)
		}, false)

	case rmCommand.FullCommand():
		commandAction(*rmTargetArg, func(uow *UnitOfWork) {
			uow.Rm(*forceRmFlag)
		}, false)

	case runCommand.FullCommand():
		commandAction(*runTargetArg, func(uow *UnitOfWork) {
			uow.Run(*runCmdArg)
		}, true)

	case createCommand.FullCommand():
		commandAction(*createTargetArg, func(uow *UnitOfWork) {
			uow.Create(*createCmdArg)
		}, true)

	case provisionCommand.FullCommand():
		commandAction(*provisionTargetArg, func(uow *UnitOfWork) {
			uow.Provision(*provisionNoCacheFlag, *provisionParallelFlag)
		}, false)

	case pullCommand.FullCommand():
		commandAction(*pullTargetArg, func(uow *UnitOfWork) {
			uow.PullImage()
		}, false)

	case logsCommand.FullCommand():
		commandAction(*logsTargetArg, func(uow *UnitOfWork) {
			uow.Logs(*followFlag, *timestampsFlag, *tailFlag, *colorizeFlag, *sinceFlag)
		}, false)

	case generateCommand.FullCommand():
		if len(*templateFlag) == 0 {
			printErrorf("ERROR: No template specified. The flag `--template` is required.\n")
			return
		}
		commandAction(*generateTargetArg, func(uow *UnitOfWork) {
			uow.Generate(*templateFlag, *outputFlag)
		}, false)

	case syncStartCommand.FullCommand():
		cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
		sync := cfg.MacSync(*syncStartVolumeArg)
		if sync == nil {
			printErrorf("ERROR: No such sync configured: %s.", *syncStartVolumeArg)
			return
		}
		sync.Start()

	case syncStopCommand.FullCommand():
		cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
		sync := cfg.MacSync(*syncStopVolumeArg)
		if sync == nil {
			printErrorf("ERROR: No such sync configured: %s.", *syncStartVolumeArg)
			return
		}
		sync.Stop()

	case syncStatusCommand.FullCommand():
		cfg = NewConfig(*configFlag, *prefixFlag, *tagFlag)
		w := new(tabwriter.Writer)
		w.Init(os.Stdout, 0, 8, 1, '\t', 0)
		fmt.Fprintln(w, "VOLUME\tCONTAINER\tSTATUS")
		for _, name := range cfg.MacSyncNames() {
			s := cfg.MacSync(name)
			syncContainerName := "-"
			if s.Exists() {
				syncContainerName = s.ContainerName()
			}
			fmt.Fprintf(w, "%s\n", s.Volume()+"\t"+syncContainerName+"\t"+s.Status())
		}
		w.Flush()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"sync"
)

var metrics_dir, scripts_dir, time_prefix string
var enable_scripts int
var core_stats map[string]interface{}
var debug bool

func main() {

	flag.IntVar(&enable_scripts, "e", 0, "Enable custom scripts execution")
	flag.StringVar(&metrics_dir, "m", "/var/log/stats_collector", "Location where metrics log files are written")
	flag.StringVar(&scripts_dir, "s", "/opt/stats_collector", "Location where custom metrics scripts are located")
	flag.StringVar(&time_prefix, "p", "SYSLOG", "Date prefix format for metric entries (RFC822Z, ISO8601, RFC3339, SYSLOG)")
	flag.BoolVar(&debug, "d", false, "Enable verbose debug mode")
	flag.Parse()

	if flag.NArg() == 0 {
		usage := `
Usage: stats-ag [OPTIONS]
Options:
	-e [ENABLE_CUSTOM_SCRIPTS] (default = 0)
	-m [METRICS_DIR] (default = /var/log/stats_collector)
	-s [CUSTOM_SCRIPTS_DIR] (default = /opt/stats_collector)
	-p [TIME_PREFIX_FORMAT] (default = SYSLOG)
	-d [DEBUG] (default = false)
	`
		fmt.Printf("%s\n", usage)
		os.Exit(0)
	}

	if debug {
		fmt.Printf(
			"\nstats-ag config values:\n---------------------------\nenable_scripts = %d\nmetrics_dir = %s\nscripts_dir = %s\ntime_prefix = %s\ndebug = %t\n\n",
			enable_scripts,
			metrics_dir,
			scripts_dir,
			time_prefix,
			debug,
		)
	}

	var wg sync.WaitGroup
	scripts, _ := ioutil.ReadDir(scripts_dir)

	core_stats = map[string]interface{}{
		"memory": GetMemStats,
		"load":   GetLoadStats,
		"disk":   GetDiskStats,
		"cpu":    GetCpuStats,
		"host":   GetHostStats,
		"net":    GetNetStats,
	}

	wg.Add(len(core_stats) + len(scripts))

	for k, _ := range core_stats {
		go func(wg *sync.WaitGroup, core_stats map[string]interface{}, method string) {
			mw := NewMetricsWriter(method, time_prefix)
			if debug {
				fmt.Printf("%s [DEBUG] fetching core stat: %s\n", getDateStamp(time_prefix), method)
			}
			res, _ := Call(core_stats, method)
			mw.Save(res[0].String())
			wg.Done()
		}(&wg, core_stats, k)
	}

	_, dir_exists_err := os.Stat(scripts_dir)
	if enable_scripts == 1 && dir_exists_err == nil {

		for _, src := range scripts {

			go func(wg *sync.WaitGroup, script_name string) {

				if debug {
					fmt.Printf("%s [DEBUG] calling custom stat: %s\n", getDateStamp(time_prefix), script_name)
				}
				cm, err := NewCustomMetric(script_name)
				if err != nil {
					fmt.Printf("%s [ERROR] NewCustomMetric() error: %s\n", getDateStamp(time_prefix), err)

					wg.Done()
					return
				}

				mw := NewMetricsWriter(cm.Name, time_prefix)
				if stats, err := cm.GetStats(); err == nil {
					mw.Save(stats)
				} else {
					fmt.Printf("%s [ERROR] GetStats() error: %s\n", getDateStamp(time_prefix), err)
				}
				wg.Done()
			}(&wg, scripts_dir+src.Name())
		}

	}

	wg.Wait()

}
<commit_msg>Added cli option to get version number and exit<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"sync"
)

var metrics_dir, scripts_dir, time_prefix string
var enable_scripts int
var core_stats map[string]interface{}
var debug bool

func main() {

	if os.Args[1] == "-v" {
		fmt.Printf("Stats-ag Version %s\n", VERSION)
		os.Exit(0)
	}

	flag.IntVar(&enable_scripts, "e", 0, "Enable custom scripts execution")
	flag.StringVar(&metrics_dir, "m", "/var/log/stats_collector", "Location where metrics log files are written")
	flag.StringVar(&scripts_dir, "s", "/opt/stats_collector", "Location where custom metrics scripts are located")
	flag.StringVar(&time_prefix, "p", "SYSLOG", "Date prefix format for metric entries (RFC822Z, ISO8601, RFC3339, SYSLOG)")
	flag.BoolVar(&debug, "d", false, "Enable verbose debug mode")
	flag.Parse()

	if flag.NArg() == 0 {
		usage := `
Usage: stats-ag [OPTIONS]
Options:
	-e [ENABLE_CUSTOM_SCRIPTS] (default = 0)
	-m [METRICS_DIR] (default = /var/log/stats_collector)
	-s [CUSTOM_SCRIPTS_DIR] (default = /opt/stats_collector)
	-p [TIME_PREFIX_FORMAT] (default = SYSLOG)
	-d [DEBUG] (default = false)
	`
		fmt.Printf("%s\n", usage)
		os.Exit(0)
	}

	if debug {
		fmt.Printf(
			"\nstats-ag config values:\n---------------------------\nenable_scripts = %d\nmetrics_dir = %s\nscripts_dir = %s\ntime_prefix = %s\ndebug = %t\n\n",
			enable_scripts,
			metrics_dir,
			scripts_dir,
			time_prefix,
			debug,
		)
	}

	var wg sync.WaitGroup
	scripts, _ := ioutil.ReadDir(scripts_dir)

	core_stats = map[string]interface{}{
		"memory": GetMemStats,
		"load":   GetLoadStats,
		"disk":   GetDiskStats,
		"cpu":    GetCpuStats,
		"host":   GetHostStats,
		"net":    GetNetStats,
	}

	wg.Add(len(core_stats) + len(scripts))

	for k, _ := range core_stats {
		go func(wg *sync.WaitGroup, core_stats map[string]interface{}, method string) {
			mw := NewMetricsWriter(method, time_prefix)
			if debug {
				fmt.Printf("%s [DEBUG] fetching core stat: %s\n", getDateStamp(time_prefix), method)
			}
			res, _ := Call(core_stats, method)
			mw.Save(res[0].String())
			wg.Done()
		}(&wg, core_stats, k)
	}

	_, dir_exists_err := os.Stat(scripts_dir)
	if enable_scripts == 1 && dir_exists_err == nil {

		for _, src := range scripts {

			go func(wg *sync.WaitGroup, script_name string) {

				if debug {
					fmt.Printf("%s [DEBUG] calling custom stat: %s\n", getDateStamp(time_prefix), script_name)
				}
				cm, err := NewCustomMetric(script_name)
				if err != nil {
					fmt.Printf("%s [ERROR] NewCustomMetric() error: %s\n", getDateStamp(time_prefix), err)

					wg.Done()
					return
				}

				mw := NewMetricsWriter(cm.Name, time_prefix)
				if stats, err := cm.GetStats(); err == nil {
					mw.Save(stats)
				} else {
					fmt.Printf("%s [ERROR] GetStats() error: %s\n", getDateStamp(time_prefix), err)
				}
				wg.Done()
			}(&wg, scripts_dir+src.Name())
		}

	}

	wg.Wait()

}
<|endoftext|>