<commit_before>/*
Copyright 2019 The Skaffold Authors

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

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

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

package docker

import (
	"context"
	"io/ioutil"
	"os"
	"sync"
	"sync/atomic"
	"testing"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/GoogleContainerTools/skaffold/testutil"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-containerregistry/pkg/name"
	v1 "github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/random"
)

func TestMain(m *testing.M) {
	// So we don't shell out to credentials helpers or try to read dockercfg
	defer func(h AuthConfigHelper) { DefaultAuthHelper = h }(DefaultAuthHelper)
	DefaultAuthHelper = testAuthHelper{}

	os.Exit(m.Run())
}

func TestPush(t *testing.T) {
	var tests = []struct {
		description    string
		imageName      string
		api            testutil.FakeAPIClient
		expectedDigest string
		shouldErr      bool
	}{
		{
			description: "push",
			imageName:   "gcr.io/scratchman",
			api: testutil.FakeAPIClient{
				TagToImageID: map[string]string{
					"gcr.io/scratchman": "sha256:imageIDabcab",
				},
			},
			expectedDigest: "sha256:7368613235363a696d61676549446162636162e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
		},
		{
			description: "stream error",
			imageName:   "gcr.io/imthescratchman",
			api: testutil.FakeAPIClient{
				ErrStream: true,
			},
			shouldErr: true,
		},
		{
			description: "image push error",
			imageName:   "gcr.io/skibabopbadopbop",
			api: testutil.FakeAPIClient{
				ErrImagePush: true,
			},
			shouldErr: true,
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			localDocker := &localDaemon{
				apiClient: &test.api,
			}

			digest, err := localDocker.Push(context.Background(), ioutil.Discard, test.imageName)

			testutil.CheckErrorAndDeepEqual(t, test.shouldErr, err, test.expectedDigest, digest)
		})
	}
}

func TestRunBuild(t *testing.T) {
	var tests = []struct {
		description string
		expected    string
		api         testutil.FakeAPIClient
		shouldErr   bool
	}{
		{
			description: "build",
			expected:    "test",
		},
		{
			description: "bad image build",
			api: testutil.FakeAPIClient{
				ErrImageBuild: true,
			},
			shouldErr: true,
		},
		{
			description: "bad return reader",
			api: testutil.FakeAPIClient{
				ErrStream: true,
			},
			shouldErr: true,
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			localDocker := &localDaemon{
				apiClient: &test.api,
			}

			_, err := localDocker.Build(context.Background(), ioutil.Discard, ".", &latest.DockerArtifact{}, "finalimage")

			testutil.CheckError(t, test.shouldErr, err)
		})
	}
}

func TestImageID(t *testing.T) {
	var tests = []struct {
		description string
		ref         string
		api         testutil.FakeAPIClient
		expected    string
		shouldErr   bool
	}{
		{
			description: "get digest",
			ref:         "identifier:latest",
			api: testutil.FakeAPIClient{
				TagToImageID: map[string]string{
					"identifier:latest": "sha256:123abc",
				},
			},
			expected: "sha256:123abc",
		},
		{
			description: "image inspect error",
			ref:         "test",
			api: testutil.FakeAPIClient{
				ErrImageInspect: true,
			},
			shouldErr: true,
		},
		{
			description: "not found",
			ref:         "somethingelse",
			shouldErr:   true,
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			localDocker := &localDaemon{
				apiClient: &test.api,
			}

			imageID, err := localDocker.ImageID(context.Background(), test.ref)

			testutil.CheckErrorAndDeepEqual(t, test.shouldErr, err, test.expected, imageID)
		})
	}
}

func TestGetBuildArgs(t *testing.T) {
	tests := []struct {
		description string
		artifact    *latest.DockerArtifact
		env         []string
		want        []string
		shouldErr   bool
	}{
		{
			description: "build args",
			artifact: &latest.DockerArtifact{
				BuildArgs: map[string]*string{
					"key1": util.StringPtr("value1"),
					"key2": nil,
					"key3": util.StringPtr("{{.FOO}}"),
				},
			},
			env:  []string{"FOO=bar"},
			want: []string{"--build-arg", "key1=value1", "--build-arg", "key2", "--build-arg", "key3=bar"},
		},
		{
			description: "build args",
			artifact: &latest.DockerArtifact{
				BuildArgs: map[string]*string{
					"key1": util.StringPtr("value1"),
					"key2": nil,
					"key3": util.StringPtr("{{.DOES_NOT_EXIST}}"),
				},
			},
			shouldErr: true,
		},
		{
			description: "cache from",
			artifact: &latest.DockerArtifact{
				CacheFrom: []string{"gcr.io/foo/bar", "baz:latest"},
			},
			want: []string{"--cache-from", "gcr.io/foo/bar", "--cache-from", "baz:latest"},
		},
		{
			description: "target",
			artifact: &latest.DockerArtifact{
				Target: "stage1",
			},
			want: []string{"--target", "stage1"},
		},
		{
			description: "network mode",
			artifact: &latest.DockerArtifact{
				NetworkMode: "Bridge",
			},
			want: []string{"--network", "bridge"},
		},
		{
			description: "all",
			artifact: &latest.DockerArtifact{
				BuildArgs: map[string]*string{
					"key1": util.StringPtr("value1"),
				},
				CacheFrom:   []string{"foo"},
				Target:      "stage1",
				NetworkMode: "None",
			},
			want: []string{"--build-arg", "key1=value1", "--cache-from", "foo", "--target", "stage1", "--network", "none"},
		},
	}
	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			util.OSEnviron = func() []string {
				return tt.env
			}
			result, err := GetBuildArgs(tt.artifact)
			if tt.shouldErr && err != nil {
				t.Errorf("expected to see an error, but saw none")
			}
			if tt.shouldErr {
				return
			}
			if diff := cmp.Diff(result, tt.want); diff != "" {
				t.Errorf("%T differ (-got, +want): %s", tt.want, diff)
			}
		})
	}
}

func TestImageExists(t *testing.T) {
	tests := []struct {
		name            string
		tagToImageID    map[string]string
		image           string
		errImageInspect bool
		expected        bool
	}{
		{
			name:         "image exists",
			image:        "image:tag",
			tagToImageID: map[string]string{"image:tag": "imageID"},
			expected:     true,
		}, {
			name:            "image does not exist",
			image:           "dne",
			errImageInspect: true,
			tagToImageID:    map[string]string{"image:tag": "imageID"},
		}, {
			name:            "error getting image",
			tagToImageID:    map[string]string{"image:tag": "imageID"},
			errImageInspect: true,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			api := &testutil.FakeAPIClient{
				ErrImageInspect: test.errImageInspect,
				TagToImageID:    test.tagToImageID,
			}

			localDocker := &localDaemon{
				apiClient: api,
			}

			actual := localDocker.ImageExists(context.Background(), test.image)
			testutil.CheckErrorAndDeepEqual(t, false, nil, test.expected, actual)
		})
	}
}

func TestRepoDigest(t *testing.T) {
	tests := []struct {
		name            string
		image           string
		tagToImageID    map[string]string
		repoDigests     []string
		errImageInspect bool
		shouldErr       bool
		expected        string
	}{
		{
			name:         "repo digest exists",
			image:        "image:tag",
			tagToImageID: map[string]string{"image:tag": "image", "image1:tag": "image1"},
			repoDigests:  []string{"repoDigest", "repoDigest1"},
			expected:     "repoDigest",
		},
		{
			name:         "repo digest does not exist",
			image:        "image",
			tagToImageID: map[string]string{},
			repoDigests:  []string{},
			shouldErr:    true,
		},
		{
			name:            "err getting repo digest",
			image:           "image:tag",
			errImageInspect: true,
			shouldErr:       true,
			tagToImageID:    map[string]string{"image:tag": "image", "image1:tag": "image1"},
			repoDigests:     []string{"repoDigest", "repoDigest1"},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			api := &testutil.FakeAPIClient{
				ErrImageInspect: test.errImageInspect,
				TagToImageID:    test.tagToImageID,
				RepoDigests:     test.repoDigests,
			}

			localDocker := &localDaemon{
				apiClient: api,
			}

			actual, err := localDocker.RepoDigest(context.Background(), test.image)
			testutil.CheckError(t, test.shouldErr, err)
			if test.shouldErr {
				return
			}
			testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, actual)
		})
	}
}

func TestInsecureRegistry(t *testing.T) {
	called := false // variable to make sure we've called our getInsecureRegistry function
	getInsecureRegistryImpl = func(_ string) (name.Reference, error) {
		called = true
		return name.Tag{}, nil
	}
	getRemoteImageImpl = func(_ name.Reference) (v1.Image, error) {
		return random.Image(0, 0)
	}

	tests := []struct {
		name               string
		image              string
		insecureRegistries map[string]bool
		insecure           bool
		shouldErr          bool
	}{
		{
			name:               "secure image",
			image:              "gcr.io/secure/image",
			insecureRegistries: map[string]bool{},
		},
		{
			name:  "insecure image",
			image: "my.insecure.registry/image",
			insecureRegistries: map[string]bool{
				"my.insecure.registry": true,
			},
			insecure: true,
		},
		{
			name:      "insecure image not provided by user",
			image:     "my.insecure.registry/image",
			insecure:  true,
			shouldErr: true,
		},
		{
			name:  "secure image provided in insecure registries list",
			image: "gcr.io/secure/image",
			insecureRegistries: map[string]bool{
				"gcr.io": true,
			},
			shouldErr: true,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_, err := remoteImage(test.image, test.insecureRegistries)
			if err != nil {
				t.Errorf("error calling remoteImage: %s", err.Error())
			}
			if test.insecure && !called { // error condition
				if !test.shouldErr {
					t.Errorf("getInsecureRegistry not called for insecure registry")
				}
			}
			if !test.insecure && called { // error condition
				if !test.shouldErr {
					t.Errorf("getInsecureRegistry called for secure registry")
				}
			}
			called = false
		})
	}
}

func TestConfigFile(t *testing.T) {
	api := &testutil.FakeAPIClient{
		TagToImageID: map[string]string{
			"gcr.io/image": "sha256:imageIDabcab",
		},
	}

	localDocker := NewLocalDaemon(api, nil, false, nil)
	cfg, err := localDocker.ConfigFile(context.Background(), "gcr.io/image")

	testutil.CheckErrorAndDeepEqual(t, false, err, "sha256:imageIDabcab", cfg.Config.Image)
}

type APICallsCounter struct {
	client.CommonAPIClient
	calls int32
}

func (c *APICallsCounter) ImageInspectWithRaw(ctx context.Context, image string) (types.ImageInspect, []byte, error) {
	atomic.AddInt32(&c.calls, 1)
	return c.CommonAPIClient.ImageInspectWithRaw(ctx, image)
}

func TestConfigFileConcurrentCalls(t *testing.T) {
	api := &APICallsCounter{
		CommonAPIClient: &testutil.FakeAPIClient{
			TagToImageID: map[string]string{
				"gcr.io/image": "sha256:imageIDabcab",
			},
		},
	}

	localDocker := NewLocalDaemon(api, nil, false, nil)

	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			localDocker.ConfigFile(context.Background(), "gcr.io/image")
			wg.Done()
		}()
	}
	wg.Wait()

	// Check that the APIClient was called only once
	testutil.CheckDeepEqual(t, int32(1), atomic.LoadInt32(&api.calls))
}
<commit_msg>Update image_test.go<commit_after>/*
Copyright 2019 The Skaffold Authors

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

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

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

package docker

import (
	"context"
	"io/ioutil"
	"os"
	"sync"
	"sync/atomic"
	"testing"

	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
	"github.com/GoogleContainerTools/skaffold/testutil"
	"github.com/docker/docker/api/types"
	"github.com/docker/docker/client"
	"github.com/google/go-cmp/cmp"
	"github.com/google/go-containerregistry/pkg/name"
	v1 "github.com/google/go-containerregistry/pkg/v1"
	"github.com/google/go-containerregistry/pkg/v1/random"
)

func TestMain(m *testing.M) {
	// So we don't shell out to credentials helpers or try to read dockercfg
	defer func(h AuthConfigHelper) { DefaultAuthHelper = h }(DefaultAuthHelper)
	DefaultAuthHelper = testAuthHelper{}

	os.Exit(m.Run())
}

func TestPush(t *testing.T) {
	var tests = []struct {
		description    string
		imageName      string
		api            testutil.FakeAPIClient
		expectedDigest string
		shouldErr      bool
	}{
		{
			description: "push",
			imageName:   "gcr.io/scratchman",
			api: testutil.FakeAPIClient{
				TagToImageID: map[string]string{
					"gcr.io/scratchman": "sha256:imageIDabcab",
				},
			},
			expectedDigest: "sha256:7368613235363a696d61676549446162636162e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
		},
		{
			description: "stream error",
			imageName:   "gcr.io/imthescratchman",
			api: testutil.FakeAPIClient{
				ErrStream: true,
			},
			shouldErr: true,
		},
		{
			description: "image push error",
			imageName:   "gcr.io/skibabopbadopbop",
			api: testutil.FakeAPIClient{
				ErrImagePush: true,
			},
			shouldErr: true,
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			localDocker := &localDaemon{
				apiClient: &test.api,
			}

			digest, err := localDocker.Push(context.Background(), ioutil.Discard, test.imageName)

			testutil.CheckErrorAndDeepEqual(t, test.shouldErr, err, test.expectedDigest, digest)
		})
	}
}

func TestRunBuild(t *testing.T) {
	var tests = []struct {
		description string
		expected    string
		api         testutil.FakeAPIClient
		shouldErr   bool
	}{
		{
			description: "build",
			expected:    "test",
		},
		{
			description: "bad image build",
			api: testutil.FakeAPIClient{
				ErrImageBuild: true,
			},
			shouldErr: true,
		},
		{
			description: "bad return reader",
			api: testutil.FakeAPIClient{
				ErrStream: true,
			},
			shouldErr: true,
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			localDocker := &localDaemon{
				apiClient: &test.api,
			}

			_, err := localDocker.Build(context.Background(), ioutil.Discard, ".", &latest.DockerArtifact{}, "finalimage")

			testutil.CheckError(t, test.shouldErr, err)
		})
	}
}

func TestImageID(t *testing.T) {
	var tests = []struct {
		description string
		ref         string
		api         testutil.FakeAPIClient
		expected    string
		shouldErr   bool
	}{
		{
			description: "get digest",
			ref:         "identifier:latest",
			api: testutil.FakeAPIClient{
				TagToImageID: map[string]string{
					"identifier:latest": "sha256:123abc",
				},
			},
			expected: "sha256:123abc",
		},
		{
			description: "image inspect error",
			ref:         "test",
			api: testutil.FakeAPIClient{
				ErrImageInspect: true,
			},
			shouldErr: true,
		},
		{
			description: "not found",
			ref:         "somethingelse",
			shouldErr:   true,
		},
	}
	for _, test := range tests {
		t.Run(test.description, func(t *testing.T) {
			localDocker := &localDaemon{
				apiClient: &test.api,
			}

			imageID, err := localDocker.ImageID(context.Background(), test.ref)

			testutil.CheckErrorAndDeepEqual(t, test.shouldErr, err, test.expected, imageID)
		})
	}
}

func TestGetBuildArgs(t *testing.T) {
	tests := []struct {
		description string
		artifact    *latest.DockerArtifact
		env         []string
		want        []string
		shouldErr   bool
	}{
		{
			description: "build args",
			artifact: &latest.DockerArtifact{
				BuildArgs: map[string]*string{
					"key1": util.StringPtr("value1"),
					"key2": nil,
					"key3": util.StringPtr("{{.FOO}}"),
				},
			},
			env:  []string{"FOO=bar"},
			want: []string{"--build-arg", "key1=value1", "--build-arg", "key2", "--build-arg", "key3=bar"},
		},
		{
			description: "build args",
			artifact: &latest.DockerArtifact{
				BuildArgs: map[string]*string{
					"key1": util.StringPtr("value1"),
					"key2": nil,
					"key3": util.StringPtr("{{.DOES_NOT_EXIST}}"),
				},
			},
			shouldErr: true,
		},
		{
			description: "cache from",
			artifact: &latest.DockerArtifact{
				CacheFrom: []string{"gcr.io/foo/bar", "baz:latest"},
			},
			want: []string{"--cache-from", "gcr.io/foo/bar", "--cache-from", "baz:latest"},
		},
		{
			description: "target",
			artifact: &latest.DockerArtifact{
				Target: "stage1",
			},
			want: []string{"--target", "stage1"},
		},
		{
			description: "network mode",
			artifact: &latest.DockerArtifact{
				NetworkMode: "Bridge",
			},
			want: []string{"--network", "bridge"},
		},
		{
			description: "no-cache",
			artifact: &latest.DockerArtifact{
				NoCache: "noCache",
			},
			want: []string{"--no-cache"},
		},
		{
			description: "all",
			artifact: &latest.DockerArtifact{
				BuildArgs: map[string]*string{
					"key1": util.StringPtr("value1"),
				},
				CacheFrom:   []string{"foo"},
				Target:      "stage1",
				NetworkMode: "None",
			},
			want: []string{"--build-arg", "key1=value1", "--cache-from", "foo", "--target", "stage1", "--network", "none"},
		},
	}
	for _, tt := range tests {
		t.Run(tt.description, func(t *testing.T) {
			util.OSEnviron = func() []string {
				return tt.env
			}
			result, err := GetBuildArgs(tt.artifact)
			if tt.shouldErr && err != nil {
				t.Errorf("expected to see an error, but saw none")
			}
			if tt.shouldErr {
				return
			}
			if diff := cmp.Diff(result, tt.want); diff != "" {
				t.Errorf("%T differ (-got, +want): %s", tt.want, diff)
			}
		})
	}
}

func TestImageExists(t *testing.T) {
	tests := []struct {
		name            string
		tagToImageID    map[string]string
		image           string
		errImageInspect bool
		expected        bool
	}{
		{
			name:         "image exists",
			image:        "image:tag",
			tagToImageID: map[string]string{"image:tag": "imageID"},
			expected:     true,
		}, {
			name:            "image does not exist",
			image:           "dne",
			errImageInspect: true,
			tagToImageID:    map[string]string{"image:tag": "imageID"},
		}, {
			name:            "error getting image",
			tagToImageID:    map[string]string{"image:tag": "imageID"},
			errImageInspect: true,
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			api := &testutil.FakeAPIClient{
				ErrImageInspect: test.errImageInspect,
				TagToImageID:    test.tagToImageID,
			}

			localDocker := &localDaemon{
				apiClient: api,
			}

			actual := localDocker.ImageExists(context.Background(), test.image)
			testutil.CheckErrorAndDeepEqual(t, false, nil, test.expected, actual)
		})
	}
}

func TestRepoDigest(t *testing.T) {
	tests := []struct {
		name            string
		image           string
		tagToImageID    map[string]string
		repoDigests     []string
		errImageInspect bool
		shouldErr       bool
		expected        string
	}{
		{
			name:         "repo digest exists",
			image:        "image:tag",
			tagToImageID: map[string]string{"image:tag": "image", "image1:tag": "image1"},
			repoDigests:  []string{"repoDigest", "repoDigest1"},
			expected:     "repoDigest",
		},
		{
			name:         "repo digest does not exist",
			image:        "image",
			tagToImageID: map[string]string{},
			repoDigests:  []string{},
			shouldErr:    true,
		},
		{
			name:            "err getting repo digest",
			image:           "image:tag",
			errImageInspect: true,
			shouldErr:       true,
			tagToImageID:    map[string]string{"image:tag": "image", "image1:tag": "image1"},
			repoDigests:     []string{"repoDigest", "repoDigest1"},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			api := &testutil.FakeAPIClient{
				ErrImageInspect: test.errImageInspect,
				TagToImageID:    test.tagToImageID,
				RepoDigests:     test.repoDigests,
			}

			localDocker := &localDaemon{
				apiClient: api,
			}

			actual, err := localDocker.RepoDigest(context.Background(), test.image)
			testutil.CheckError(t, test.shouldErr, err)
			if test.shouldErr {
				return
			}
			testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, actual)
		})
	}
}

func TestInsecureRegistry(t *testing.T) {
	called := false // variable to make sure we've called our getInsecureRegistry function
	getInsecureRegistryImpl = func(_ string) (name.Reference, error) {
		called = true
		return name.Tag{}, nil
	}
	getRemoteImageImpl = func(_ name.Reference) (v1.Image, error) {
		return random.Image(0, 0)
	}

	tests := []struct {
		name               string
		image              string
		insecureRegistries map[string]bool
		insecure           bool
		shouldErr          bool
	}{
		{
			name:               "secure image",
			image:              "gcr.io/secure/image",
			insecureRegistries: map[string]bool{},
		},
		{
			name:  "insecure image",
			image: "my.insecure.registry/image",
			insecureRegistries: map[string]bool{
				"my.insecure.registry": true,
			},
			insecure: true,
		},
		{
			name:      "insecure image not provided by user",
			image:     "my.insecure.registry/image",
			insecure:  true,
			shouldErr: true,
		},
		{
			name:  "secure image provided in insecure registries list",
			image: "gcr.io/secure/image",
			insecureRegistries: map[string]bool{
				"gcr.io": true,
			},
			shouldErr: true,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			_, err := remoteImage(test.image, test.insecureRegistries)
			if err != nil {
				t.Errorf("error calling remoteImage: %s", err.Error())
			}
			if test.insecure && !called { // error condition
				if !test.shouldErr {
					t.Errorf("getInsecureRegistry not called for insecure registry")
				}
			}
			if !test.insecure && called { // error condition
				if !test.shouldErr {
					t.Errorf("getInsecureRegistry called for secure registry")
				}
			}
			called = false
		})
	}
}

func TestConfigFile(t *testing.T) {
	api := &testutil.FakeAPIClient{
		TagToImageID: map[string]string{
			"gcr.io/image": "sha256:imageIDabcab",
		},
	}

	localDocker := NewLocalDaemon(api, nil, false, nil)
	cfg, err := localDocker.ConfigFile(context.Background(), "gcr.io/image")

	testutil.CheckErrorAndDeepEqual(t, false, err, "sha256:imageIDabcab", cfg.Config.Image)
}

type APICallsCounter struct {
	client.CommonAPIClient
	calls int32
}

func (c *APICallsCounter) ImageInspectWithRaw(ctx context.Context, image string) (types.ImageInspect, []byte, error) {
	atomic.AddInt32(&c.calls, 1)
	return c.CommonAPIClient.ImageInspectWithRaw(ctx, image)
}

func TestConfigFileConcurrentCalls(t *testing.T) {
	api := &APICallsCounter{
		CommonAPIClient: &testutil.FakeAPIClient{
			TagToImageID: map[string]string{
				"gcr.io/image": "sha256:imageIDabcab",
			},
		},
	}

	localDocker := NewLocalDaemon(api, nil, false, nil)

	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func() {
			localDocker.ConfigFile(context.Background(), "gcr.io/image")
			wg.Done()
		}()
	}
	wg.Wait()

	// Check that the APIClient was called only once
	testutil.CheckDeepEqual(t, int32(1), atomic.LoadInt32(&api.calls))
}
<|endoftext|>


<commit_before>// Copyright 2017 The nats-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kubernetes

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/nats-io/nats-operator/pkg/constants"
	"github.com/nats-io/nats-operator/pkg/debug/local"
	"github.com/nats-io/nats-operator/pkg/spec"
	"github.com/nats-io/nats-operator/pkg/util/retryutil"

	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/client-go/kubernetes/scheme"
	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // for gcp auth
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	TolerateUnreadyEndpointsAnnotation = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
	versionAnnotationKey               = "nats.version"
)

const (
	LabelAppKey            = "app"
	LabelAppValue          = "nats"
	LabelClusterNameKey    = "nats_cluster"
	LabelClusterVersionKey = "nats_version"
)

func GetNATSVersion(pod *v1.Pod) string {
	return pod.Annotations[versionAnnotationKey]
}

func SetNATSVersion(pod *v1.Pod, version string) {
	pod.Annotations[versionAnnotationKey] = version
}

func GetPodNames(pods []*v1.Pod) []string {
	if len(pods) == 0 {
		return nil
	}
	res := []string{}
	for _, p := range pods {
		res = append(res, p.Name)
	}
	return res
}

func MakeNATSImage(version string) string {
	return fmt.Sprintf("nats:%v", version)
}

func PodWithNodeSelector(p *v1.Pod, ns map[string]string) *v1.Pod {
	p.Spec.NodeSelector = ns
	return p
}

func createService(kubecli corev1client.CoreV1Interface, svcName, clusterName, ns, clusterIP string, ports []v1.ServicePort, owner metav1.OwnerReference, selectors map[string]string, tolerateUnready bool) error {
	svc := newNatsServiceManifest(svcName, clusterName, clusterIP, ports, selectors, tolerateUnready)
	addOwnerRefToObject(svc.GetObjectMeta(), owner)
	_, err := kubecli.Services(ns).Create(svc)
	return err
}

func CreateClientService(kubecli corev1client.CoreV1Interface, clusterName, ns string, owner metav1.OwnerReference) error {
	ports := []v1.ServicePort{{
		Name:       "client",
		Port:       constants.ClientPort,
		TargetPort: intstr.FromInt(constants.ClientPort),
		Protocol:   v1.ProtocolTCP,
	}}
	selectors := LabelsForCluster(clusterName)
	return createService(kubecli, clusterName, clusterName, ns, "", ports, owner, selectors, false)
}

func ManagementServiceName(clusterName string) string {
	return clusterName + "-mgmt"
}

// CreateMgmtService creates an headless service for NATS management purposes.
func CreateMgmtService(kubecli corev1client.CoreV1Interface, clusterName, clusterVersion, ns string, owner metav1.OwnerReference) error {
	ports := []v1.ServicePort{
		{
			Name:       "cluster",
			Port:       constants.ClusterPort,
			TargetPort: intstr.FromInt(constants.ClusterPort),
			Protocol:   v1.ProtocolTCP,
		},
		{
			Name:       "monitoring",
			Port:       constants.MonitoringPort,
			TargetPort: intstr.FromInt(constants.MonitoringPort),
			Protocol:   v1.ProtocolTCP,		},
	}
	selectors := LabelsForCluster(clusterName)
	selectors[LabelClusterVersionKey] = clusterVersion
	return createService(kubecli, ManagementServiceName(clusterName), clusterName, ns, v1.ClusterIPNone, ports, owner, selectors, true)
}

// CreateAndWaitPod is an util for testing.
// We should eventually get rid of this in critical code path and move it to test util.
func CreateAndWaitPod(kubecli corev1client.CoreV1Interface, ns string, pod *v1.Pod, timeout time.Duration) (*v1.Pod, error) {
	_, err := kubecli.Pods(ns).Create(pod)
	if err != nil {
		return nil, err
	}

	interval := 5 * time.Second
	var retPod *v1.Pod
	err = retryutil.Retry(interval, int(timeout/(interval)), func() (bool, error) {
		retPod, err = kubecli.Pods(ns).Get(pod.Name, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		switch retPod.Status.Phase {
		case v1.PodRunning:
			return true, nil
		case v1.PodPending:
			return false, nil
		default:
			return false, fmt.Errorf("unexpected pod status.phase: %v", retPod.Status.Phase)
		}
	})

	if err != nil {
		if retryutil.IsRetryFailure(err) {
			return nil, fmt.Errorf("failed to wait pod running, it is still pending: %v", err)
		}
		return nil, fmt.Errorf("failed to wait pod running: %v", err)
	}

	return retPod, nil
}

func newNatsServiceManifest(svcName, clusterName, clusterIP string, ports []v1.ServicePort, selectors map[string]string, tolerateUnready bool) *v1.Service {
	labels := map[string]string{
		LabelAppKey:         LabelAppValue,
		LabelClusterNameKey: clusterName,
	}

	annotations := make(map[string]string)
	if tolerateUnready == true {
		annotations[TolerateUnreadyEndpointsAnnotation] = "true"
	}

	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:        svcName,
			Labels:      labels,
			Annotations: annotations,
		},
		Spec: v1.ServiceSpec{
			Ports:     ports,
			Selector:  selectors,
			ClusterIP: clusterIP,
		},
	}
	return svc
}

func addOwnerRefToObject(o metav1.Object, r metav1.OwnerReference) {
	o.SetOwnerReferences(append(o.GetOwnerReferences(), r))
}

// NewNatsPodSpec returns a NATS peer pod specification, based on the cluster specification.
func NewNatsPodSpec(clusterName string, cs spec.ClusterSpec, owner metav1.OwnerReference) *v1.Pod {
	labels := map[string]string{
		LabelAppKey:            "nats",
		LabelClusterNameKey:    clusterName,
		LabelClusterVersionKey: cs.Version,
	}

	volumes := []v1.Volume{}

	container := natsPodContainer(clusterName, cs.Version)
	container = containerWithLivenessProbe(container, natsLivenessProbe(cs.TLS.IsSecureClient()))
	container = containerWithReadinessProbe(container, natsReadinessProbe(clusterName))

	if cs.Pod != nil {
		container = containerWithRequirements(container, cs.Pod.Resources)
	}

	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Labels:       labels,
			GenerateName: fmt.Sprintf("nats-%s-", clusterName),
			Annotations:  map[string]string{},
		},
		Spec: v1.PodSpec{
			Containers:    []v1.Container{container},
			RestartPolicy: v1.RestartPolicyNever,
			Volumes:       volumes,
		},
	}

	applyPodPolicy(clusterName, pod, cs.Pod)

	SetNATSVersion(pod, cs.Version)

	addOwnerRefToObject(pod.GetObjectMeta(), owner)

	return pod
}

func MustNewKubeClient() corev1client.CoreV1Interface {
	var (
		cfg *rest.Config
		err error
	)

	if len(local.KubeConfigPath) == 0 {
		cfg, err = InClusterConfig()
	} else {
		cfg, err = clientcmd.BuildConfigFromFlags("", local.KubeConfigPath)
	}

	if err != nil {
		panic(err)
	}

	return corev1client.NewForConfigOrDie(cfg)
}

func InClusterConfig() (*rest.Config, error) {
	// Work around https://github.com/kubernetes/kubernetes/issues/40973
	if len(os.Getenv("KUBERNETES_SERVICE_HOST")) == 0 {
		addrs, err := net.LookupHost("kubernetes.default.svc")
		if err != nil {
			panic(err)
		}
		os.Setenv("KUBERNETES_SERVICE_HOST", addrs[0])
	}
	if len(os.Getenv("KUBERNETES_SERVICE_PORT")) == 0 {
		os.Setenv("KUBERNETES_SERVICE_PORT", "443")
	}
	return rest.InClusterConfig()
}

func IsKubernetesResourceAlreadyExistError(err error) bool {
	return apierrors.IsAlreadyExists(err)
}

func IsKubernetesResourceNotFoundError(err error) bool {
	return apierrors.IsNotFound(err)
}

// We are using internal api types for cluster related.
func ClusterListOpt(clusterName string) metav1.ListOptions {
	return metav1.ListOptions{
		LabelSelector: labels.SelectorFromSet(LabelsForCluster(clusterName)).String(),
	}
}

func LabelsForCluster(clusterName string) map[string]string {
	return map[string]string{
		LabelAppKey:         LabelAppValue,
		LabelClusterNameKey: clusterName,
	}
}

func CreatePatch(o, n, datastruct interface{}) ([]byte, error) {
	oldData, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	newData, err := json.Marshal(n)
	if err != nil {
		return nil, err
	}
	return strategicpatch.CreateTwoWayMergePatch(oldData, newData, datastruct)
}

func ClonePod(p *v1.Pod) *v1.Pod {
	np, err := scheme.Scheme.DeepCopy(p)
	if err != nil {
		panic("cannot deep copy pod")
	}
	return np.(*v1.Pod)
}

func CloneSvc(s *v1.Service) *v1.Service {
	ns, err := scheme.Scheme.DeepCopy(s)
	if err != nil {
		panic("cannot deep copy svc")
	}
	return ns.(*v1.Service)
}

// mergeLables merges l2 into l1. Conflicting label will be skipped.
func mergeLabels(l1, l2 map[string]string) {
	for k, v := range l2 {
		if _, ok := l1[k]; ok {
			continue
		}
		l1[k] = v
	}
}
<commit_msg>Use official image minus liveness probe<commit_after>// Copyright 2017 The nats-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kubernetes

import (
	"encoding/json"
	"fmt"
	"net"
	"os"
	"time"

	"github.com/nats-io/nats-operator/pkg/constants"
	"github.com/nats-io/nats-operator/pkg/debug/local"
	"github.com/nats-io/nats-operator/pkg/spec"
	"github.com/nats-io/nats-operator/pkg/util/retryutil"

	"k8s.io/api/core/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/apimachinery/pkg/util/strategicpatch"
	"k8s.io/client-go/kubernetes/scheme"
	corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // for gcp auth
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	TolerateUnreadyEndpointsAnnotation = "service.alpha.kubernetes.io/tolerate-unready-endpoints"
	versionAnnotationKey               = "nats.version"
)

const (
	LabelAppKey            = "app"
	LabelAppValue          = "nats"
	LabelClusterNameKey    = "nats_cluster"
	LabelClusterVersionKey = "nats_version"
)

func GetNATSVersion(pod *v1.Pod) string {
	return pod.Annotations[versionAnnotationKey]
}

func SetNATSVersion(pod *v1.Pod, version string) {
	pod.Annotations[versionAnnotationKey] = version
}

func GetPodNames(pods []*v1.Pod) []string {
	if len(pods) == 0 {
		return nil
	}
	res := []string{}
	for _, p := range pods {
		res = append(res, p.Name)
	}
	return res
}

func MakeNATSImage(version string) string {
	return fmt.Sprintf("nats:%v", version)
}

func PodWithNodeSelector(p *v1.Pod, ns map[string]string) *v1.Pod {
	p.Spec.NodeSelector = ns
	return p
}

func createService(kubecli corev1client.CoreV1Interface, svcName, clusterName, ns, clusterIP string, ports []v1.ServicePort, owner metav1.OwnerReference, selectors map[string]string, tolerateUnready bool) error {
	svc := newNatsServiceManifest(svcName, clusterName, clusterIP, ports, selectors, tolerateUnready)
	addOwnerRefToObject(svc.GetObjectMeta(), owner)
	_, err := kubecli.Services(ns).Create(svc)
	return err
}

func CreateClientService(kubecli corev1client.CoreV1Interface, clusterName, ns string, owner metav1.OwnerReference) error {
	ports := []v1.ServicePort{{
		Name:       "client",
		Port:       constants.ClientPort,
		TargetPort: intstr.FromInt(constants.ClientPort),
		Protocol:   v1.ProtocolTCP,
	}}
	selectors := LabelsForCluster(clusterName)
	return createService(kubecli, clusterName, clusterName, ns, "", ports, owner, selectors, false)
}

func ManagementServiceName(clusterName string) string {
	return clusterName + "-mgmt"
}

// CreateMgmtService creates an headless service for NATS management purposes.
func CreateMgmtService(kubecli corev1client.CoreV1Interface, clusterName, clusterVersion, ns string, owner metav1.OwnerReference) error {
	ports := []v1.ServicePort{
		{
			Name:       "cluster",
			Port:       constants.ClusterPort,
			TargetPort: intstr.FromInt(constants.ClusterPort),
			Protocol:   v1.ProtocolTCP,
		},
		{
			Name:       "monitoring",
			Port:       constants.MonitoringPort,
			TargetPort: intstr.FromInt(constants.MonitoringPort),
			Protocol:   v1.ProtocolTCP,		},
	}
	selectors := LabelsForCluster(clusterName)
	selectors[LabelClusterVersionKey] = clusterVersion
	return createService(kubecli, ManagementServiceName(clusterName), clusterName, ns, v1.ClusterIPNone, ports, owner, selectors, true)
}

// CreateAndWaitPod is an util for testing.
// We should eventually get rid of this in critical code path and move it to test util.
func CreateAndWaitPod(kubecli corev1client.CoreV1Interface, ns string, pod *v1.Pod, timeout time.Duration) (*v1.Pod, error) {
	_, err := kubecli.Pods(ns).Create(pod)
	if err != nil {
		return nil, err
	}

	interval := 5 * time.Second
	var retPod *v1.Pod
	err = retryutil.Retry(interval, int(timeout/(interval)), func() (bool, error) {
		retPod, err = kubecli.Pods(ns).Get(pod.Name, metav1.GetOptions{})
		if err != nil {
			return false, err
		}
		switch retPod.Status.Phase {
		case v1.PodRunning:
			return true, nil
		case v1.PodPending:
			return false, nil
		default:
			return false, fmt.Errorf("unexpected pod status.phase: %v", retPod.Status.Phase)
		}
	})

	if err != nil {
		if retryutil.IsRetryFailure(err) {
			return nil, fmt.Errorf("failed to wait pod running, it is still pending: %v", err)
		}
		return nil, fmt.Errorf("failed to wait pod running: %v", err)
	}

	return retPod, nil
}

func newNatsServiceManifest(svcName, clusterName, clusterIP string, ports []v1.ServicePort, selectors map[string]string, tolerateUnready bool) *v1.Service {
	labels := map[string]string{
		LabelAppKey:         LabelAppValue,
		LabelClusterNameKey: clusterName,
	}

	annotations := make(map[string]string)
	if tolerateUnready == true {
		annotations[TolerateUnreadyEndpointsAnnotation] = "true"
	}

	svc := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:        svcName,
			Labels:      labels,
			Annotations: annotations,
		},
		Spec: v1.ServiceSpec{
			Ports:     ports,
			Selector:  selectors,
			ClusterIP: clusterIP,
		},
	}
	return svc
}

func addOwnerRefToObject(o metav1.Object, r metav1.OwnerReference) {
	o.SetOwnerReferences(append(o.GetOwnerReferences(), r))
}

// NewNatsPodSpec returns a NATS peer pod specification, based on the cluster specification.
func NewNatsPodSpec(clusterName string, cs spec.ClusterSpec, owner metav1.OwnerReference) *v1.Pod {
	labels := map[string]string{
		LabelAppKey:            "nats",
		LabelClusterNameKey:    clusterName,
		LabelClusterVersionKey: cs.Version,
	}

	volumes := []v1.Volume{}

	container := natsPodContainer(clusterName, cs.Version)
	container = containerWithLivenessProbe(container, natsLivenessProbe(cs.TLS.IsSecureClient()))

	if cs.Pod != nil {
		container = containerWithRequirements(container, cs.Pod.Resources)
	}

	pod := &v1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Labels:       labels,
			GenerateName: fmt.Sprintf("nats-%s-", clusterName),
			Annotations:  map[string]string{},
		},
		Spec: v1.PodSpec{
			Containers:    []v1.Container{container},
			RestartPolicy: v1.RestartPolicyNever,
			Volumes:       volumes,
		},
	}

	applyPodPolicy(clusterName, pod, cs.Pod)

	SetNATSVersion(pod, cs.Version)

	addOwnerRefToObject(pod.GetObjectMeta(), owner)

	return pod
}

func MustNewKubeClient() corev1client.CoreV1Interface {
	var (
		cfg *rest.Config
		err error
	)

	if len(local.KubeConfigPath) == 0 {
		cfg, err = InClusterConfig()
	} else {
		cfg, err = clientcmd.BuildConfigFromFlags("", local.KubeConfigPath)
	}

	if err != nil {
		panic(err)
	}

	return corev1client.NewForConfigOrDie(cfg)
}

func InClusterConfig() (*rest.Config, error) {
	// Work around https://github.com/kubernetes/kubernetes/issues/40973
	if len(os.Getenv("KUBERNETES_SERVICE_HOST")) == 0 {
		addrs, err := net.LookupHost("kubernetes.default.svc")
		if err != nil {
			panic(err)
		}
		os.Setenv("KUBERNETES_SERVICE_HOST", addrs[0])
	}
	if len(os.Getenv("KUBERNETES_SERVICE_PORT")) == 0 {
		os.Setenv("KUBERNETES_SERVICE_PORT", "443")
	}
	return rest.InClusterConfig()
}

func IsKubernetesResourceAlreadyExistError(err error) bool {
	return apierrors.IsAlreadyExists(err)
}

func IsKubernetesResourceNotFoundError(err error) bool {
	return apierrors.IsNotFound(err)
}

// We are using internal api types for cluster related.
func ClusterListOpt(clusterName string) metav1.ListOptions {
	return metav1.ListOptions{
		LabelSelector: labels.SelectorFromSet(LabelsForCluster(clusterName)).String(),
	}
}

func LabelsForCluster(clusterName string) map[string]string {
	return map[string]string{
		LabelAppKey:         LabelAppValue,
		LabelClusterNameKey: clusterName,
	}
}

func CreatePatch(o, n, datastruct interface{}) ([]byte, error) {
	oldData, err := json.Marshal(o)
	if err != nil {
		return nil, err
	}
	newData, err := json.Marshal(n)
	if err != nil {
		return nil, err
	}
	return strategicpatch.CreateTwoWayMergePatch(oldData, newData, datastruct)
}

func ClonePod(p *v1.Pod) *v1.Pod {
	np, err := scheme.Scheme.DeepCopy(p)
	if err != nil {
		panic("cannot deep copy pod")
	}
	return np.(*v1.Pod)
}

func CloneSvc(s *v1.Service) *v1.Service {
	ns, err := scheme.Scheme.DeepCopy(s)
	if err != nil {
		panic("cannot deep copy svc")
	}
	return ns.(*v1.Service)
}

// mergeLables merges l2 into l1. Conflicting label will be skipped.
func mergeLabels(l1, l2 map[string]string) {
	for k, v := range l2 {
		if _, ok := l1[k]; ok {
			continue
		}
		l1[k] = v
	}
}
<|endoftext|>


<commit_before>package gocode

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

const TestDirectory = "./_testing"

var (
	tests []Test
	conf  *Config
)

func init() {
	var err error
	conf, err = newConfig()
	if err != nil {
		panic(err)
	}
	tests, err = loadTests()
	if err != nil {
		panic(err)
	}
}

func TestInit(t *testing.T) {
	if _, err := newConfig(); err != nil {
		t.Fatal(err)
	}
	if _, err := loadTests(); err != nil {
		t.Fatal(err)
	}
}

func TestGocode(t *testing.T) {
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_1(t *testing.T) {
	t.Parallel()
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_2(t *testing.T) {
	t.Parallel()
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_3(t *testing.T) {
	t.Parallel()
	conf.GOPATH = "" // Alter GOPATH
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_4(t *testing.T) {
	t.Parallel()
	conf.GOPATH = os.Getenv("GOPATH") // Alter GOPATH
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func BenchmarkOne(b *testing.B) {
	t := tests[0]
	for i := 0; i < b.N; i++ {
		_ = conf.Complete(t.File, t.Name, t.Cursor)
	}
}

func BenchmarkTen(b *testing.B) {
	if len(tests) < 10 {
		b.Fatal("Expected 10+ test cases")
	}
	tt := tests[:10]
	for i := 0; i < b.N; i++ {
		for _, t := range tt {
			_ = conf.Complete(t.File, t.Name, t.Cursor)
		}
	}
}

func BenchmarkAll(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, t := range tests {
			_ = conf.Complete(t.File, t.Name, t.Cursor)
		}
	}
}

type Test struct {
	Name   string
	File   []byte
	Cursor int
	Result []string
}

func (t Test) Check(conf *Config) error {
	fn := filepath.Base(filepath.Dir(t.Name))
	cs := conf.Complete(t.File, t.Name, t.Cursor)
	if len(cs) != len(t.Result) {
		return fmt.Errorf("count: expected %d got %d: %s", len(t.Result), len(cs), fn)
	}
	for i, c := range cs {
		r := t.Result[i]
		if c.String() != r {
			return fmt.Errorf("candidate: expected '%s' got '%s': %s", r, c, fn)
		}
	}
	return nil
}

func loadTests() ([]Test, error) {
	var tests []Test
	list, err := ioutil.ReadDir(TestDirectory)
	if err != nil {
		return nil, err
	}
	for _, fi := range list {
		if fi.IsDir() {
			test, err := newTest(filepath.Join(TestDirectory, fi.Name()))
			if err != nil {
				return nil, err
			}
			tests = append(tests, *test)
		}
	}
	return tests, nil
}

func newTest(path string) (*Test, error) {
	list, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, err
	}
	t := Test{Cursor: -1}
	for _, fi := range list {
		fn := fi.Name()
		switch fn {
		case "test.go.in":
			t.Name = filepath.Join(path, fn)
			t.File, err = ioutil.ReadFile(t.Name)
			if err != nil {
				return nil, err
			}
		case "out.expected":
			t.Result, err = newResult(filepath.Join(path, fn))
			if err != nil {
				return nil, err
			}
		default:
			if strings.HasPrefix(fn, "cursor") {
				n := strings.IndexByte(fn, '.')
				if n == -1 {
					return nil, fmt.Errorf("error parsing cursor file: %s", fn)
				}
				t.Cursor, err = strconv.Atoi(fn[n+1:])
				if err != nil {
					return nil, err
				}
			}
		}
	}
	if t.Cursor == -1 {
		return nil, fmt.Errorf("no cursor file in directory: %s", path)
	}
	if t.Name == "" {
		return nil, fmt.Errorf("no test file in directory: %s", path)
	}
	if t.File == nil {
		return nil, fmt.Errorf("nil test file in directory: %s", path)
	}
	return &t, nil
}

func newResult(path string) ([]string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	n := bytes.IndexByte(b, '\n')
	if n == len(b)-1 {
		return []string{}, nil
	}
	var s []string
	for _, b := range bytes.Split(b[n+1:], []byte{'\n'}) {
		if len(b) > 1 {
			s = append(s, string(bytes.TrimSpace(b)))
		}
	}
	return s, nil
}

func newConfig() (*Config, error) {
	c := Config{
		GOROOT: runtime.GOROOT(),
		GOPATH: os.Getenv("GOPATH"),
	}
	if c.GOROOT == "" {
		return nil, fmt.Errorf("GOROOT must be set")
	}
	if c.GOPATH == "" {
		return nil, fmt.Errorf("GOPATH must be set")
	}
	return &c, nil
}
<commit_msg>Skip parallel tests<commit_after>package gocode

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"testing"
)

const TestDirectory = "./_testing"

var (
	tests []Test
	conf  *Config
)

func init() {
	var err error
	conf, err = newConfig()
	if err != nil {
		panic(err)
	}
	tests, err = loadTests()
	if err != nil {
		panic(err)
	}
}

func TestInit(t *testing.T) {
	if _, err := newConfig(); err != nil {
		t.Fatal(err)
	}
	if _, err := loadTests(); err != nil {
		t.Fatal(err)
	}
}

func TestGocode(t *testing.T) {
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_1(t *testing.T) {
	t.Skip("Skip: Parallel_1")
	t.Parallel()
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_2(t *testing.T) {
	t.Skip("Skip: Parallel_2")
	t.Parallel()
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_3(t *testing.T) {
	t.Skip("Skip: Parallel_3")
	t.Parallel()
	conf.GOPATH = "" // Alter GOPATH
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func TestParallel_4(t *testing.T) {
	t.Skip("Skip: Parallel_4")
	t.Parallel()
	conf.GOPATH = os.Getenv("GOPATH") // Alter GOPATH
	for _, test := range tests {
		if err := test.Check(conf); err != nil {
			t.Fatal(err)
		}
	}
}

func BenchmarkOne(b *testing.B) {
	t := tests[0]
	for i := 0; i < b.N; i++ {
		_ = conf.Complete(t.File, t.Name, t.Cursor)
	}
}

func BenchmarkTen(b *testing.B) {
	if len(tests) < 10 {
		b.Fatal("Expected 10+ test cases")
	}
	tt := tests[:10]
	for i := 0; i < b.N; i++ {
		for _, t := range tt {
			_ = conf.Complete(t.File, t.Name, t.Cursor)
		}
	}
}

func BenchmarkAll(b *testing.B) {
	for i := 0; i < b.N; i++ {
		for _, t := range tests {
			_ = conf.Complete(t.File, t.Name, t.Cursor)
		}
	}
}

type Test struct {
	Name   string
	File   []byte
	Cursor int
	Result []string
}

func (t Test) Check(conf *Config) error {
	if conf == nil {
		return errors.New("Check: nil Config")
	}
	fn := filepath.Base(filepath.Dir(t.Name))
	cs := conf.Complete(t.File, t.Name, t.Cursor)
	if len(cs) != len(t.Result) {
		return fmt.Errorf("count: expected %d got %d: %s", len(t.Result), len(cs), fn)
	}
	for i, c := range cs {
		r := t.Result[i]
		if c.String() != r {
			return fmt.Errorf("candidate: expected '%s' got '%s': %s", r, c, fn)
		}
	}
	return nil
}

func loadTests() ([]Test, error) {
	var tests []Test
	list, err := ioutil.ReadDir(TestDirectory)
	if err != nil {
		return nil, err
	}
	for _, fi := range list {
		if fi.IsDir() {
			test, err := newTest(filepath.Join(TestDirectory, fi.Name()))
			if err != nil {
				return nil, err
			}
			tests = append(tests, *test)
		}
	}
	return tests, nil
}

func newTest(path string) (*Test, error) {
	list, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, err
	}
	t := Test{Cursor: -1}
	for _, fi := range list {
		fn := fi.Name()
		switch fn {
		case "test.go.in":
			t.Name = filepath.Join(path, fn)
			t.File, err = ioutil.ReadFile(t.Name)
			if err != nil {
				return nil, err
			}
		case "out.expected":
			t.Result, err = newResult(filepath.Join(path, fn))
			if err != nil {
				return nil, err
			}
		default:
			if strings.HasPrefix(fn, "cursor") {
				n := strings.IndexByte(fn, '.')
				if n == -1 {
					return nil, fmt.Errorf("error parsing cursor file: %s", fn)
				}
				t.Cursor, err = strconv.Atoi(fn[n+1:])
				if err != nil {
					return nil, err
				}
			}
		}
	}
	if t.Cursor == -1 {
		return nil, fmt.Errorf("no cursor file in directory: %s", path)
	}
	if t.Name == "" {
		return nil, fmt.Errorf("no test file in directory: %s", path)
	}
	if t.File == nil {
		return nil, fmt.Errorf("nil test file in directory: %s", path)
	}
	return &t, nil
}

func newResult(path string) ([]string, error) {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	n := bytes.IndexByte(b, '\n')
	if n == len(b)-1 {
		return []string{}, nil
	}
	var s []string
	for _, b := range bytes.Split(b[n+1:], []byte{'\n'}) {
		if len(b) > 1 {
			s = append(s, string(bytes.TrimSpace(b)))
		}
	}
	return s, nil
}

func newConfig() (*Config, error) {
	c := Config{
		GOROOT: runtime.GOROOT(),
		GOPATH: os.Getenv("GOPATH"),
	}
	if c.GOROOT == "" {
		return nil, fmt.Errorf("GOROOT must be set")
	}
	if c.GOPATH == "" {
		return nil, fmt.Errorf("GOPATH must be set")
	}
	return &c, nil
}
<|endoftext|>


<commit_before>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build go1.7

package azure

import (
	"encoding/xml"
	"fmt"
)

var (
	azureImageReplicateURL   = "services/images/%s/replicate"
	azureImageUnreplicateURL = "services/images/%s/unreplicate"
)

type ReplicationInput struct {
	XMLName         xml.Name `xml:"http://schemas.microsoft.com/windowsazure ReplicationInput"`
	TargetLocations []string `xml:"TargetLocations>Region"`
	Offer           string   `xml:"ComputeImageAttributes>Offer"`
	Sku             string   `xml:"ComputeImageAttributes>Sku"`
	Version         string   `xml:"ComputeImageAttributes>Version"`
}

func (a *API) ReplicateImage(image, offer, sku, version string, regions ...string) error {
	ri := ReplicationInput{
		TargetLocations: regions,
		Offer:           offer,
		Sku:             sku,
		Version:         version,
	}

	data, err := xml.Marshal(&ri)
	if err != nil {
		return err
	}

	url := fmt.Sprintf(azureImageReplicateURL, image)

	op, err := a.client.SendAzurePutRequest(url, "", data)
	if err != nil {
		return err
	}

	return a.client.WaitForOperation(op, nil)
}

func (a *API) UnreplicateImage(image string) error {
	url := fmt.Sprintf(azureImageUnreplicateURL, image)
	op, err := a.client.SendAzurePutRequest(url, "", []byte{})
	if err != nil {
		return err
	}

	return a.client.WaitForOperation(op, nil)
}
<commit_msg>platform/api/azure: add Locations() to get Azure Locations<commit_after>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build go1.7

package azure

import (
	"encoding/xml"
	"fmt"

	"github.com/Azure/azure-sdk-for-go/management/location"
)

var (
	azureImageReplicateURL   = "services/images/%s/replicate"
	azureImageUnreplicateURL = "services/images/%s/unreplicate"
)

type ReplicationInput struct {
	XMLName         xml.Name `xml:"http://schemas.microsoft.com/windowsazure ReplicationInput"`
	TargetLocations []string `xml:"TargetLocations>Region"`
	Offer           string   `xml:"ComputeImageAttributes>Offer"`
	Sku             string   `xml:"ComputeImageAttributes>Sku"`
	Version         string   `xml:"ComputeImageAttributes>Version"`
}

// Locations returns a slice of Azure Locations, useful for replicating to all Locations.
func (a *API) Locations() ([]string, error) {
	lc := location.NewClient(a.client)

	llr, err := lc.ListLocations()
	if err != nil {
		return nil, err
	}

	var locations []string

	for _, l := range llr.Locations {
		locations = append(locations, l.Name)
	}

	return locations, nil
}

func (a *API) ReplicateImage(image, offer, sku, version string, regions ...string) error {
	ri := ReplicationInput{
		TargetLocations: regions,
		Offer:           offer,
		Sku:             sku,
		Version:         version,
	}

	data, err := xml.Marshal(&ri)
	if err != nil {
		return err
	}

	url := fmt.Sprintf(azureImageReplicateURL, image)

	op, err := a.client.SendAzurePutRequest(url, "", data)
	if err != nil {
		return err
	}

	return a.client.WaitForOperation(op, nil)
}

func (a *API) UnreplicateImage(image string) error {
	url := fmt.Sprintf(azureImageUnreplicateURL, image)
	op, err := a.client.SendAzurePutRequest(url, "", []byte{})
	if err != nil {
		return err
	}

	return a.client.WaitForOperation(op, nil)
}
<|endoftext|>


<commit_before>package tmarrayencoder

import (
	"time"
)

type TMArrayEncoderUnlengthPure struct {
}

var _ TMArrayEncoderUnlength = TMArrayEncoderUnlengthPure{}

//var basis tmenc
func (a TMArrayEncoderUnlengthPure) EncodeBoolArray(b []bool) (ary []byte) {
	for _, e := range b {
		ary = append(ary, elementEncoder.EncodeBool(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeFloat32Array(f []float32) (ary []byte) {
	for _, e := range f {
		ary = append(ary, elementEncoder.EncodeFloat32(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeFloat64Array(f []float64) (ary []byte) {
	for _, e := range f {
		ary = append(ary, elementEncoder.EncodeFloat64(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt8Array(i []int8) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt8(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt16Array(i []int16) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt16(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt32Array(i []int32) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt32(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt64Array(i []int64) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt64(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeOctetArray(b []byte) (ary []byte) {
	for _, e := range b {
		ary = append(ary, elementEncoder.EncodeOctet(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeTimeArray(t []time.Time) (ary []byte) {
	for _, e := range t {
		ary = append(ary, elementEncoder.EncodeTime(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint8Array(i []uint8) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint8(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint16Array(i []uint16) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint16(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint32Array(i []uint32) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint32(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint64Array(i []uint64) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint64(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUvarintArray(i []uint) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUvarint(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeVarintArray(i []int) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeVarint(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) PrefixStatus(TMArrayEncoderUnlength) {
}
<commit_msg>fix typo<commit_after>package tmarrayencoder

import (
	"time"
)

type TMArrayEncoderUnlengthPure struct {
}

var _ TMArrayEncoderUnlength = TMArrayEncoderUnlengthPure{}

func (a TMArrayEncoderUnlengthPure) EncodeBoolArray(b []bool) (ary []byte) {
	for _, e := range b {
		ary = append(ary, elementEncoder.EncodeBool(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeFloat32Array(f []float32) (ary []byte) {
	for _, e := range f {
		ary = append(ary, elementEncoder.EncodeFloat32(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeFloat64Array(f []float64) (ary []byte) {
	for _, e := range f {
		ary = append(ary, elementEncoder.EncodeFloat64(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt8Array(i []int8) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt8(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt16Array(i []int16) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt16(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt32Array(i []int32) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt32(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeInt64Array(i []int64) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeInt64(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeOctetArray(b []byte) (ary []byte) {
	for _, e := range b {
		ary = append(ary, elementEncoder.EncodeOctet(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeTimeArray(t []time.Time) (ary []byte) {
	for _, e := range t {
		ary = append(ary, elementEncoder.EncodeTime(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint8Array(i []uint8) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint8(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint16Array(i []uint16) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint16(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint32Array(i []uint32) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint32(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUint64Array(i []uint64) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUint64(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeUvarintArray(i []uint) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeUvarint(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) EncodeVarintArray(i []int) (ary []byte) {
	for _, e := range i {
		ary = append(ary, elementEncoder.EncodeVarint(e)...)
	}
	return
}

func (a TMArrayEncoderUnlengthPure) PrefixStatus(TMArrayEncoderUnlength) {
}
<|endoftext|>


<commit_before>package input

import "fmt"
import "os"
import "bufio"
import "strings"
import "strconv"

const IntMax = int(^uint(0) >> 1)
const IntMin = -IntMax - 1

// Line reading closure, to save on bufio.NewReader overhead.
// Prompts with a supplied string.
func GetReader() func(string) (string) {
    in := bufio.NewReader(os.Stdin)
    return func(p string) (string) {
        fmt.Print(p)
        s, _ := in.ReadString('\n')
        s = strings.TrimRight(s, "\n\r")
        return s
    }
}

//Type parser func shorthand...
type Parser func(string, interface{}) error

//Parses a line from stdin with specified func,
//retrying indefinitely on failure
func Read(p string,
          f Parser,
          x interface{}) {
    in := bufio.NewReader(os.Stdin)
    for {
        fmt.Print(p)
        s, e := in.ReadString('\n')
        s = strings.TrimRight(s, "\n\r")
        if e = f(s, x); e == nil {
            break
        }
        fmt.Println(e)
    }
}

//Returns a Parser for ints within specified range
func IntParser(min, max int) Parser {
    return func(s string, x interface{}) error {
        i, e := strconv.Atoi(s)
        if e != nil {
            return fmt.Errorf("Must be an integer")
        }
        if i < min || i > max {
            return fmt.Errorf("Must be in range %d-%d", min, max)
        }
        *x.(*int) = i
        return nil
    }
}

func ReadInt(p string, min, max int) (i int) {
    Read(p, GetIntParser(min, max), &i)
    return
}
<commit_msg>Fixed GetIntParser() -> IntParser() in input.ReadInt()<commit_after>package input

import "fmt"
import "os"
import "bufio"
import "strings"
import "strconv"

const IntMax = int(^uint(0) >> 1)
const IntMin = -IntMax - 1

// Line reading closure, to save on bufio.NewReader overhead.
// Prompts with a supplied string.
func GetReader() func(string) (string) {
    in := bufio.NewReader(os.Stdin)
    return func(p string) (string) {
        fmt.Print(p)
        s, _ := in.ReadString('\n')
        s = strings.TrimRight(s, "\n\r")
        return s
    }
}

//Type parser func shorthand...
type Parser func(string, interface{}) error

//Parses a line from stdin with specified func,
//retrying indefinitely on failure
func Read(p string,
          f Parser,
          x interface{}) {
    in := bufio.NewReader(os.Stdin)
    for {
        fmt.Print(p)
        s, e := in.ReadString('\n')
        s = strings.TrimRight(s, "\n\r")
        if e = f(s, x); e == nil {
            break
        }
        fmt.Println(e)
    }
}

//Returns a Parser for ints within specified range
func IntParser(min, max int) Parser {
    return func(s string, x interface{}) error {
        i, e := strconv.Atoi(s)
        if e != nil {
            return fmt.Errorf("Must be an integer")
        }
        if i < min || i > max {
            return fmt.Errorf("Must be in range %d-%d", min, max)
        }
        *x.(*int) = i
        return nil
    }
}

func ReadInt(p string, min, max int) (i int) {
    Read(p, IntParser(min, max), &i)
    return
}
<|endoftext|>


<commit_before>package lfs

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"
	"time"

	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/rubyist/tracerx"
)

// When scanning diffs e.g. parseLogOutputToPointers, which direction of diff to include
// data from, i.e. '+' or '-'. Depending on what you're scanning for either might be useful
type LogDiffDirection byte

const (
	LogDiffAdditions = LogDiffDirection('+') // include '+' diffs
	LogDiffDeletions = LogDiffDirection('-') // include '-' diffs
)

var (
	// Arguments to append to a git log call which will limit the output to
	// lfs changes and format the output suitable for parseLogOutput.. method(s)
	logLfsSearchArgs = []string{
		"-G", "oid sha256:", // only diffs which include an lfs file SHA change
		"-p",   // include diff so we can read the SHA
		"-U12", // Make sure diff context is always big enough to support 10 extension lines to get whole pointer
		`--format=lfs-commit-sha: %H %P`, // just a predictable commit header we can detect
	}
)

func scanUnpushed(remote string) (*PointerChannelWrapper, error) {
	logArgs := []string{"log",
		"--branches", "--tags", // include all locally referenced commits
		"--not"} // but exclude everything that comes after

	if len(remote) == 0 {
		logArgs = append(logArgs, "--remotes")
	} else {
		logArgs = append(logArgs, fmt.Sprintf("--remotes=%v", remote))
	}

	// Add standard search args to find lfs references
	logArgs = append(logArgs, logLfsSearchArgs...)

	cmd, err := startCommand("git", logArgs...)
	if err != nil {
		return nil, err
	}

	cmd.Stdin.Close()

	pchan := make(chan *WrappedPointer, chanBufSize)
	errchan := make(chan error, 1)

	go func() {
		parseLogOutputToPointers(cmd.Stdout, LogDiffAdditions, nil, nil, pchan)
		stderr, _ := ioutil.ReadAll(cmd.Stderr)
		err := cmd.Wait()
		if err != nil {
			errchan <- fmt.Errorf("Error in git log: %v %v", err, string(stderr))
		}
		close(pchan)
		close(errchan)
	}()

	return NewPointerChannelWrapper(pchan, errchan), nil
}

// logPreviousVersions scans history for all previous versions of LFS pointers
// from 'since' up to (but not including) the final state at ref
func logPreviousSHAs(ref string, since time.Time) (*PointerChannelWrapper, error) {
	logArgs := []string{"log",
		fmt.Sprintf("--since=%v", git.FormatGitDate(since)),
	}
	// Add standard search args to find lfs references
	logArgs = append(logArgs, logLfsSearchArgs...)
	// ending at ref
	logArgs = append(logArgs, ref)

	cmd, err := startCommand("git", logArgs...)
	if err != nil {
		return nil, err
	}

	cmd.Stdin.Close()

	pchan := make(chan *WrappedPointer, chanBufSize)
	errchan := make(chan error, 1)

	// we pull out deletions, since we want the previous SHAs at commits in the range
	// this means we pick up all previous versions that could have been checked
	// out in the date range, not just if the commit which *introduced* them is in the range
	go func() {
		parseLogOutputToPointers(cmd.Stdout, LogDiffDeletions, nil, nil, pchan)
		stderr, _ := ioutil.ReadAll(cmd.Stderr)
		err := cmd.Wait()
		if err != nil {
			errchan <- fmt.Errorf("Error in git log: %v %v", err, string(stderr))
		}
		close(pchan)
		close(errchan)
	}()

	return NewPointerChannelWrapper(pchan, errchan), nil
}

type logScanner struct {
	s            *bufio.Scanner
	dir          LogDiffDirection
	includePaths []string
	excludePaths []string
	pointer      *WrappedPointer

	pointerData         *bytes.Buffer
	currentFilename     string
	currentFileIncluded bool

	commitHeaderRegex    *regexp.Regexp
	fileHeaderRegex      *regexp.Regexp
	fileMergeHeaderRegex *regexp.Regexp
	pointerDataRegex     *regexp.Regexp
}

func newLogScanner(r io.Reader, dir LogDiffDirection, includePaths, excludePaths []string) *logScanner {
	return &logScanner{
		s:                    bufio.NewScanner(r),
		dir:                  dir,
		includePaths:         includePaths,
		excludePaths:         excludePaths,
		pointerData:          &bytes.Buffer{},
		currentFileIncluded:  true,
		commitHeaderRegex:    regexp.MustCompile(`^lfs-commit-sha: ([A-Fa-f0-9]{40})(?: ([A-Fa-f0-9]{40}))*`),
		fileHeaderRegex:      regexp.MustCompile(`diff --git a\/(.+?)\s+b\/(.+)`),
		fileMergeHeaderRegex: regexp.MustCompile(`diff --cc (.+)`),
		pointerDataRegex:     regexp.MustCompile(`^([\+\- ])(version https://git-lfs|oid sha256|size|ext-).*$`),
	}
}

func (s *logScanner) Pointer() *WrappedPointer {
	return s.pointer
}

func (s *logScanner) Err() error {
	return s.s.Err()
}

func (s *logScanner) Scan() bool {
	s.pointer = nil
	p, canScan := s.scan()
	s.pointer = p
	return canScan
}

// Utility func used at several points below (keep in narrow scope)
func (s *logScanner) finishLastPointer() *WrappedPointer {
	if s.pointerData.Len() > 0 {
		if s.currentFileIncluded {
			p, err := DecodePointer(s.pointerData)
			if err == nil {
				return &WrappedPointer{Name: s.currentFilename, Pointer: p}
			} else {
				tracerx.Printf("Unable to parse pointer from log: %v", err)
			}
		}
		s.pointerData.Reset()
	}
	return nil
}

// parseLogOutputToPointers parses log output formatted as per logLfsSearchArgs & return pointers
// log: a stream of output from git log with at least logLfsSearchArgs specified
// dir: whether to include results from + or - diffs
// includePaths, excludePaths: filter the results by filename
// results: a channel which will receive the pointers (caller must close)
func parseLogOutputToPointers(log io.Reader, dir LogDiffDirection,
	includePaths, excludePaths []string, results chan *WrappedPointer) {

	// For each commit we'll get something like this:
	/*
		lfs-commit-sha: 60fde3d23553e10a55e2a32ed18c20f65edd91e7 e2eaf1c10b57da7b98eb5d722ec5912ddeb53ea1

		diff --git a/1D_Noise.png b/1D_Noise.png
		new file mode 100644
		index 0000000..2622b4a
		--- /dev/null
		+++ b/1D_Noise.png
		@@ -0,0 +1,3 @@
		+version https://git-lfs.github.com/spec/v1
		+oid sha256:f5d84da40ab1f6aa28df2b2bf1ade2cdcd4397133f903c12b4106641b10e1ed6
		+size 1289
	*/
	// There can be multiple diffs per commit (multiple binaries)
	// Also when a binary is changed the diff will include a '-' line for the old SHA

	// Define regexes to capture commit & diff headers
	commitHeaderRegex := regexp.MustCompile(`^lfs-commit-sha: ([A-Fa-f0-9]{40})(?: ([A-Fa-f0-9]{40}))*`)
	fileHeaderRegex := regexp.MustCompile(`diff --git a\/(.+?)\s+b\/(.+)`)
	fileMergeHeaderRegex := regexp.MustCompile(`diff --cc (.+)`)
	pointerDataRegex := regexp.MustCompile(`^([\+\- ])(version https://git-lfs|oid sha256|size|ext-).*$`)
	var pointerData bytes.Buffer
	var currentFilename string
	currentFileIncluded := true

	// Utility func used at several points below (keep in narrow scope)
	finishLastPointer := func() {
		if pointerData.Len() > 0 {
			if currentFileIncluded {
				p, err := DecodePointer(&pointerData)
				if err == nil {
					results <- &WrappedPointer{Name: currentFilename, Pointer: p}
				} else {
					tracerx.Printf("Unable to parse pointer from log: %v", err)
				}
			}
			pointerData.Reset()
		}
	}

	scanner := bufio.NewScanner(log)
	for scanner.Scan() {
		line := scanner.Text()
		if match := commitHeaderRegex.FindStringSubmatch(line); match != nil {
			// Currently we're not pulling out commit groupings, but could if we wanted
			// This just acts as a delimiter for finishing a multiline pointer
			finishLastPointer()

		} else if match := fileHeaderRegex.FindStringSubmatch(line); match != nil {
			// Finding a regular file header
			finishLastPointer()
			// Pertinent file name depends on whether we're listening to additions or removals
			if dir == LogDiffAdditions {
				currentFilename = match[2]
			} else {
				currentFilename = match[1]
			}
			currentFileIncluded = tools.FilenamePassesIncludeExcludeFilter(currentFilename, includePaths, excludePaths)
		} else if match := fileMergeHeaderRegex.FindStringSubmatch(line); match != nil {
			// Git merge file header is a little different, only one file
			finishLastPointer()
			currentFilename = match[1]
			currentFileIncluded = tools.FilenamePassesIncludeExcludeFilter(currentFilename, includePaths, excludePaths)
		} else if currentFileIncluded {
			if match := pointerDataRegex.FindStringSubmatch(line); match != nil {
				// An LFS pointer data line
				// Include only the entirety of one side of the diff
				// -U3 will ensure we always get all of it, even if only
				// the SHA changed (version & size the same)
				changeType := match[1][0]
				// Always include unchanged context lines (normally just the version line)
				if LogDiffDirection(changeType) == dir || changeType == ' ' {
					// Must skip diff +/- marker
					pointerData.WriteString(line[1:])
					pointerData.WriteString("\n") // newline was stripped off by scanner
				}
			}
		}
	}
	// Final pointer if in progress
	finishLastPointer()
}

func (s *logScanner) scan() (*WrappedPointer, bool) {
	for s.s.Scan() {
		line := s.s.Text()

		if match := s.commitHeaderRegex.FindStringSubmatch(line); match != nil {
			// Currently we're not pulling out commit groupings, but could if we wanted
			// This just acts as a delimiter for finishing a multiline pointer
			if p := s.finishLastPointer(); p != nil {
				return p, true
			}
		} else if match := s.fileHeaderRegex.FindStringSubmatch(line); match != nil {
			// Finding a regular file header
			p := s.finishLastPointer()
			// Pertinent file name depends on whether we're listening to additions or removals
			if s.dir == LogDiffAdditions {
				s.currentFilename = match[2]
			} else {
				s.currentFilename = match[1]
			}
			s.currentFileIncluded = tools.FilenamePassesIncludeExcludeFilter(s.currentFilename, s.includePaths, s.excludePaths)

			if p != nil {
				return p, true
			}
		} else if match := s.fileMergeHeaderRegex.FindStringSubmatch(line); match != nil {
			// Git merge file header is a little different, only one file
			p := s.finishLastPointer()
			s.currentFilename = match[1]
			s.currentFileIncluded = tools.FilenamePassesIncludeExcludeFilter(s.currentFilename, s.includePaths, s.excludePaths)

			if p != nil {
				return p, true
			}
		} else if s.currentFileIncluded {
			if match := s.pointerDataRegex.FindStringSubmatch(line); match != nil {
				// An LFS pointer data line
				// Include only the entirety of one side of the diff
				// -U3 will ensure we always get all of it, even if only
				// the SHA changed (version & size the same)
				changeType := match[1][0]

				// Always include unchanged context lines (normally just the version line)
				if LogDiffDirection(changeType) == s.dir || changeType == ' ' {
					// Must skip diff +/- marker
					s.pointerData.WriteString(line[1:])
					s.pointerData.WriteString("\n") // newline was stripped off by scanner
				}
			}
		}
	}

	if p := s.finishLastPointer(); p != nil {
		return p, true
	}

	return nil, false
}
<commit_msg>remove the old parseLogOutputToPointers()<commit_after>package lfs

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"regexp"
	"time"

	"github.com/git-lfs/git-lfs/git"
	"github.com/git-lfs/git-lfs/tools"
	"github.com/rubyist/tracerx"
)

// When scanning diffs e.g. parseLogOutputToPointers, which direction of diff to include
// data from, i.e. '+' or '-'. Depending on what you're scanning for either might be useful
type LogDiffDirection byte

const (
	LogDiffAdditions = LogDiffDirection('+') // include '+' diffs
	LogDiffDeletions = LogDiffDirection('-') // include '-' diffs
)

var (
	// Arguments to append to a git log call which will limit the output to
	// lfs changes and format the output suitable for parseLogOutput.. method(s)
	logLfsSearchArgs = []string{
		"-G", "oid sha256:", // only diffs which include an lfs file SHA change
		"-p",   // include diff so we can read the SHA
		"-U12", // Make sure diff context is always big enough to support 10 extension lines to get whole pointer
		`--format=lfs-commit-sha: %H %P`, // just a predictable commit header we can detect
	}
)

func scanUnpushed(remote string) (*PointerChannelWrapper, error) {
	logArgs := []string{"log",
		"--branches", "--tags", // include all locally referenced commits
		"--not"} // but exclude everything that comes after

	if len(remote) == 0 {
		logArgs = append(logArgs, "--remotes")
	} else {
		logArgs = append(logArgs, fmt.Sprintf("--remotes=%v", remote))
	}

	// Add standard search args to find lfs references
	logArgs = append(logArgs, logLfsSearchArgs...)

	cmd, err := startCommand("git", logArgs...)
	if err != nil {
		return nil, err
	}

	cmd.Stdin.Close()

	pchan := make(chan *WrappedPointer, chanBufSize)
	errchan := make(chan error, 1)

	go func() {
		parseLogOutputToPointers(cmd.Stdout, LogDiffAdditions, nil, nil, pchan)
		stderr, _ := ioutil.ReadAll(cmd.Stderr)
		err := cmd.Wait()
		if err != nil {
			errchan <- fmt.Errorf("Error in git log: %v %v", err, string(stderr))
		}
		close(pchan)
		close(errchan)
	}()

	return NewPointerChannelWrapper(pchan, errchan), nil
}

// logPreviousVersions scans history for all previous versions of LFS pointers
// from 'since' up to (but not including) the final state at ref
func logPreviousSHAs(ref string, since time.Time) (*PointerChannelWrapper, error) {
	logArgs := []string{"log",
		fmt.Sprintf("--since=%v", git.FormatGitDate(since)),
	}
	// Add standard search args to find lfs references
	logArgs = append(logArgs, logLfsSearchArgs...)
	// ending at ref
	logArgs = append(logArgs, ref)

	cmd, err := startCommand("git", logArgs...)
	if err != nil {
		return nil, err
	}

	cmd.Stdin.Close()

	pchan := make(chan *WrappedPointer, chanBufSize)
	errchan := make(chan error, 1)

	// we pull out deletions, since we want the previous SHAs at commits in the range
	// this means we pick up all previous versions that could have been checked
	// out in the date range, not just if the commit which *introduced* them is in the range
	go func() {
		parseLogOutputToPointers(cmd.Stdout, LogDiffDeletions, nil, nil, pchan)
		stderr, _ := ioutil.ReadAll(cmd.Stderr)
		err := cmd.Wait()
		if err != nil {
			errchan <- fmt.Errorf("Error in git log: %v %v", err, string(stderr))
		}
		close(pchan)
		close(errchan)
	}()

	return NewPointerChannelWrapper(pchan, errchan), nil
}

func parseLogOutputToPointers(log io.Reader, dir LogDiffDirection,
	includePaths, excludePaths []string, results chan *WrappedPointer) {
	scanner := newLogScanner(log, dir, includePaths, excludePaths)
	for scanner.Scan() {
		if p := scanner.Pointer(); p != nil {
			results <- p
		}
	}
}

// logScanner parses log output formatted as per logLfsSearchArgs & returns
// pointers.
type logScanner struct {
	s            *bufio.Scanner
	dir          LogDiffDirection
	includePaths []string
	excludePaths []string
	pointer      *WrappedPointer

	pointerData         *bytes.Buffer
	currentFilename     string
	currentFileIncluded bool

	commitHeaderRegex    *regexp.Regexp
	fileHeaderRegex      *regexp.Regexp
	fileMergeHeaderRegex *regexp.Regexp
	pointerDataRegex     *regexp.Regexp
}

// r: a stream of output from git log with at least logLfsSearchArgs specified
// dir: whether to include results from + or - diffs
// includePaths, excludePaths: filter the results by filename
func newLogScanner(r io.Reader, dir LogDiffDirection, includePaths, excludePaths []string) *logScanner {
	return &logScanner{
		s:                   bufio.NewScanner(r),
		dir:                 dir,
		includePaths:        includePaths,
		excludePaths:        excludePaths,
		pointerData:         &bytes.Buffer{},
		currentFileIncluded: true,

		// no need to compile these regexes on every `git-lfs` call, just ones that
		// use the scanner.
		commitHeaderRegex:    regexp.MustCompile(`^lfs-commit-sha: ([A-Fa-f0-9]{40})(?: ([A-Fa-f0-9]{40}))*`),
		fileHeaderRegex:      regexp.MustCompile(`diff --git a\/(.+?)\s+b\/(.+)`),
		fileMergeHeaderRegex: regexp.MustCompile(`diff --cc (.+)`),
		pointerDataRegex:     regexp.MustCompile(`^([\+\- ])(version https://git-lfs|oid sha256|size|ext-).*$`),
	}
}

func (s *logScanner) Pointer() *WrappedPointer {
	return s.pointer
}

func (s *logScanner) Err() error {
	return s.s.Err()
}

func (s *logScanner) Scan() bool {
	s.pointer = nil
	p, canScan := s.scan()
	s.pointer = p
	return canScan
}

// Utility func used at several points below (keep in narrow scope)
func (s *logScanner) finishLastPointer() *WrappedPointer {
	if s.pointerData.Len() > 0 {
		if s.currentFileIncluded {
			p, err := DecodePointer(s.pointerData)
			if err == nil {
				return &WrappedPointer{Name: s.currentFilename, Pointer: p}
			} else {
				tracerx.Printf("Unable to parse pointer from log: %v", err)
			}
		}
		s.pointerData.Reset()
	}
	return nil
}

// For each commit we'll get something like this:
/*
	lfs-commit-sha: 60fde3d23553e10a55e2a32ed18c20f65edd91e7 e2eaf1c10b57da7b98eb5d722ec5912ddeb53ea1

	diff --git a/1D_Noise.png b/1D_Noise.png
	new file mode 100644
	index 0000000..2622b4a
	--- /dev/null
	+++ b/1D_Noise.png
	@@ -0,0 +1,3 @@
	+version https://git-lfs.github.com/spec/v1
	+oid sha256:f5d84da40ab1f6aa28df2b2bf1ade2cdcd4397133f903c12b4106641b10e1ed6
	+size 1289
*/
// There can be multiple diffs per commit (multiple binaries)
// Also when a binary is changed the diff will include a '-' line for the old SHA
func (s *logScanner) scan() (*WrappedPointer, bool) {
	for s.s.Scan() {
		line := s.s.Text()

		if match := s.commitHeaderRegex.FindStringSubmatch(line); match != nil {
			// Currently we're not pulling out commit groupings, but could if we wanted
			// This just acts as a delimiter for finishing a multiline pointer
			if p := s.finishLastPointer(); p != nil {
				return p, true
			}
		} else if match := s.fileHeaderRegex.FindStringSubmatch(line); match != nil {
			// Finding a regular file header
			p := s.finishLastPointer()
			// Pertinent file name depends on whether we're listening to additions or removals
			if s.dir == LogDiffAdditions {
				s.currentFilename = match[2]
			} else {
				s.currentFilename = match[1]
			}
			s.currentFileIncluded = tools.FilenamePassesIncludeExcludeFilter(s.currentFilename, s.includePaths, s.excludePaths)

			if p != nil {
				return p, true
			}
		} else if match := s.fileMergeHeaderRegex.FindStringSubmatch(line); match != nil {
			// Git merge file header is a little different, only one file
			p := s.finishLastPointer()
			s.currentFilename = match[1]
			s.currentFileIncluded = tools.FilenamePassesIncludeExcludeFilter(s.currentFilename, s.includePaths, s.excludePaths)

			if p != nil {
				return p, true
			}
		} else if s.currentFileIncluded {
			if match := s.pointerDataRegex.FindStringSubmatch(line); match != nil {
				// An LFS pointer data line
				// Include only the entirety of one side of the diff
				// -U3 will ensure we always get all of it, even if only
				// the SHA changed (version & size the same)
				changeType := match[1][0]

				// Always include unchanged context lines (normally just the version line)
				if LogDiffDirection(changeType) == s.dir || changeType == ' ' {
					// Must skip diff +/- marker
					s.pointerData.WriteString(line[1:])
					s.pointerData.WriteString("\n") // newline was stripped off by scanner
				}
			}
		}
	}

	if p := s.finishLastPointer(); p != nil {
		return p, true
	}

	return nil, false
}
<|endoftext|>


<commit_before>package main

import (
	"reflect"

	"github.com/ninjasphere/go-ninja/channels"
)

// TODO: This should check if this is a motion or presence sensor, and output channels accordingly.
// TODO: This should expose battery alarm
// TODO: This should expose tamper alarm
// BUG: Multiple status events are being received for a single actual event sent from the device
type IASZoneCluster struct {
	Channel
	presence *channels.PresenceChannel
}

type IASZoneStatus struct {
	Alarm1             bool
	Alarm2             bool
	Tamper             bool
	Battery            bool
	SupervisionReports bool
	RestoreReports     bool
	Trouble            bool
	AC                 bool
	Reserved1          bool
	Reserved2          bool
	Reserved3          bool
	Reserved4          bool
	Reserved5          bool
	Reserved6          bool
	Reserved7          bool
	Reserved8          bool
}

func (c *IASZoneCluster) init() error {
	log.Debugf("Initialising IAS Zone cluster of device % X", *c.device.deviceInfo.IeeeAddress)

	stateChange := c.device.driver.gatewayConn.OnZoneState(*c.device.deviceInfo.IeeeAddress)

	go func() {
		for {
			state := <-stateChange

			if state.SrcAddress.EndpointId == c.endpoint.EndpointId {
				log.Infof("IAS Zone change. Device:%X State:%v", *c.device.deviceInfo.IeeeAddress, state)
			}

			status := &IASZoneStatus{}

			readMask(int(*state.ZoneStatus), status)

			c.presence.SendState(status.Alarm1)
		}
	}()

	c.presence = channels.NewPresenceChannel()
	err = c.device.driver.Conn.ExportChannel(c.device, c.presence, c.ID+"presence")
	if err != nil {
		log.Fatalf("Failed to announce presence channel: %s", err)
	}

	return nil
}

func readMask(mask int, target interface{}) {

	targetValue := reflect.Indirect(reflect.ValueOf(target))

	for i := 0; i < targetValue.NumField(); i++ {
		value := (mask >> uint(i) & 1) > 0
		f := targetValue.Field(i)
		if f.CanSet() {
			targetValue.Field(i).SetBool(value)
		}
	}
}
<commit_msg>Use fixed listener api<commit_after>package main

import (
	"reflect"

	"github.com/ninjasphere/go-ninja/channels"
)

// TODO: This should check if this is a motion or presence sensor, and output channels accordingly.
// TODO: This should expose battery alarm
// TODO: This should expose tamper alarm
// BUG: Multiple status events are being received for a single actual event sent from the device
type IASZoneCluster struct {
	Channel
	presence *channels.PresenceChannel
}

type IASZoneStatus struct {
	Alarm1             bool
	Alarm2             bool
	Tamper             bool
	Battery            bool
	SupervisionReports bool
	RestoreReports     bool
	Trouble            bool
	AC                 bool
	Reserved1          bool
	Reserved2          bool
	Reserved3          bool
	Reserved4          bool
	Reserved5          bool
	Reserved6          bool
	Reserved7          bool
	Reserved8          bool
}

func (c *IASZoneCluster) init() error {
	log.Debugf("Initialising IAS Zone cluster of device % X", *c.device.deviceInfo.IeeeAddress)

	stateChange := c.device.driver.gatewayConn.OnZoneState(*c.device.deviceInfo.IeeeAddress, *c.endpoint.EndpointId)

	go func() {
		for {
			state := <-stateChange

			status := &IASZoneStatus{}

			readMask(int(*state.ZoneStatus), status)

			c.presence.SendState(status.Alarm1)
		}
	}()

	c.presence = channels.NewPresenceChannel()
	err = c.device.driver.Conn.ExportChannel(c.device, c.presence, c.ID+"presence")
	if err != nil {
		log.Fatalf("Failed to announce presence channel: %s", err)
	}

	return nil
}

func readMask(mask int, target interface{}) {

	targetValue := reflect.Indirect(reflect.ValueOf(target))

	for i := 0; i < targetValue.NumField(); i++ {
		value := (mask >> uint(i) & 1) > 0
		f := targetValue.Field(i)
		if f.CanSet() {
			targetValue.Field(i).SetBool(value)
		}
	}
}
<|endoftext|>


<commit_before>package genetics

import (
	"github.com/yaricom/goNEAT/neat/network"
	"io"
	"fmt"
	"github.com/yaricom/goNEAT/neat"
)

// The Gene class in this system specifies a "Connection Gene."
// Nodes are represented using the NNode class, which serves as both a genotypic and phenotypic representation of nodes.
// Genetic Representation of connections uses this special class because it calls for special operations better served
// by a specific genetic representation.
// A Gene object in this system specifies a link between two nodes along with an "innovation number" which tells when
// in the history of a population the gene first arose. This allows the system to track innovations and use those to
// determine which organisms are compatible (i.e. in the same species).
// A mutation_num gives a rough sense of how much mutation the gene has experienced since it originally appeared
// (Since it was first innovated). In the current implementation the mutation number is the same as the weight.
type Gene struct {
	// The link between nodes
	Link *network.Link
	// The current innovation number for this gene
	InnovationNum int64
	// Used to see how much mutation has changed the link
	MutationNum float64
	// If true the gene is enabled
	IsEnabled bool
}

// Creates new Gene
func NewGene(weight float64, in_node, out_node *network.NNode, recurrent bool, inov_num int64, mut_num float64) *Gene  {
	return newGene(network.NewLink(weight, in_node, out_node, recurrent), inov_num, mut_num, true)
}

// Creates new Gene with Trait
func NewGeneWithTrait(trait *neat.Trait, weight float64, in_node, out_node *network.NNode,
			recurrent bool, inov_num int64, mut_num float64) *Gene  {
	return newGene(network.NewLinkWithTrait(trait, weight, in_node, out_node, recurrent), inov_num, mut_num, true)
}

// Construct a gene off of another gene as a duplicate
func NewGeneCopy(g *Gene, trait *neat.Trait, in_node, out_node *network.NNode) *Gene {
	return newGene(network.NewLinkWithTrait(trait, g.Link.Weight, in_node, out_node, g.Link.IsRecurrent),
		g.InnovationNum, g.MutationNum, true)
}

func newGene(link *network.Link, inov_num int64, mut_num float64, enabled bool) *Gene {
	return &Gene{
		Link:link,
		InnovationNum:inov_num,
		MutationNum:mut_num,
		IsEnabled:enabled,
	}
}

// Writes Gene to the provided writer
func (g *Gene) Write(w io.Writer)  {
	link := g.Link
	traitId := 0
	if link.Trait != nil {
		traitId = link.Trait.Id
	}
	inNodeId := link.InNode.Id
	outNodeId := link.OutNode.Id
	weight := link.Weight
	recurrent := link.IsRecurrent
	innov_num := g.InnovationNum
	mut_num := g.MutationNum
	enabled := g.IsEnabled

	fmt.Fprintf(w, "%d %d %d %g %t %d %g %t",
		traitId, inNodeId, outNodeId, weight, recurrent, innov_num, mut_num, enabled)
}

func (g *Gene) String() string  {
	enabl_str := ""
	if !g.IsEnabled {
		enabl_str = " -DISABLED-"
	}
	recurr_str := ""
	if g.Link.IsRecurrent {
		recurr_str = " -RECUR-"
	}
	trait_str := ""
	if g.Link.Trait != nil {
		trait_str = fmt.Sprintf(" Link's trait_id: %d", g.Link.Trait.Id)
	}
	return fmt.Sprintf("[Link (%4d ->%4d) INNOV (%4d, % .3f) Weight: % .3f %s%s%s : %s->%s]",
		g.Link.InNode.Id, g.Link.OutNode.Id, g.InnovationNum, g.MutationNum, g.Link.Weight,
		trait_str, enabl_str, recurr_str, g.Link.InNode, g.Link.OutNode)
}
<commit_msg>Removed Write - now it's in genome write<commit_after>package genetics

import (
	"github.com/yaricom/goNEAT/neat/network"
	"fmt"
	"github.com/yaricom/goNEAT/neat"
)

// The Gene class in this system specifies a "Connection Gene."
// Nodes are represented using the NNode class, which serves as both a genotypic and phenotypic representation of nodes.
// Genetic Representation of connections uses this special class because it calls for special operations better served
// by a specific genetic representation.
// A Gene object in this system specifies a link between two nodes along with an "innovation number" which tells when
// in the history of a population the gene first arose. This allows the system to track innovations and use those to
// determine which organisms are compatible (i.e. in the same species).
// A mutation_num gives a rough sense of how much mutation the gene has experienced since it originally appeared
// (Since it was first innovated). In the current implementation the mutation number is the same as the weight.
type Gene struct {
	// The link between nodes
	Link          *network.Link
	// The current innovation number for this gene
	InnovationNum int64
	// Used to see how much mutation has changed the link
	MutationNum   float64
	// If true the gene is enabled
	IsEnabled     bool
}

// Creates new Gene
func NewGene(weight float64, in_node, out_node *network.NNode, recurrent bool, inov_num int64, mut_num float64) *Gene {
	return newGene(network.NewLink(weight, in_node, out_node, recurrent), inov_num, mut_num, true)
}

// Creates new Gene with Trait
func NewGeneWithTrait(trait *neat.Trait, weight float64, in_node, out_node *network.NNode,
recurrent bool, inov_num int64, mut_num float64) *Gene {
	return newGene(network.NewLinkWithTrait(trait, weight, in_node, out_node, recurrent), inov_num, mut_num, true)
}

// Construct a gene off of another gene as a duplicate
func NewGeneCopy(g *Gene, trait *neat.Trait, in_node, out_node *network.NNode) *Gene {
	return newGene(network.NewLinkWithTrait(trait, g.Link.Weight, in_node, out_node, g.Link.IsRecurrent),
		g.InnovationNum, g.MutationNum, true)
}

func newGene(link *network.Link, inov_num int64, mut_num float64, enabled bool) *Gene {
	return &Gene{
		Link:link,
		InnovationNum:inov_num,
		MutationNum:mut_num,
		IsEnabled:enabled,
	}
}

func (g *Gene) String() string {
	enabl_str := ""
	if !g.IsEnabled {
		enabl_str = " -DISABLED-"
	}
	recurr_str := ""
	if g.Link.IsRecurrent {
		recurr_str = " -RECUR-"
	}
	trait_str := ""
	if g.Link.Trait != nil {
		trait_str = fmt.Sprintf(" Link's trait_id: %d", g.Link.Trait.Id)
	}
	return fmt.Sprintf("[Link (%4d ->%4d) INNOV (%4d, % .3f) Weight: % .3f %s%s%s : %s->%s]",
		g.Link.InNode.Id, g.Link.OutNode.Id, g.InnovationNum, g.MutationNum, g.Link.Weight,
		trait_str, enabl_str, recurr_str, g.Link.InNode, g.Link.OutNode)
}
<|endoftext|>