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

package version_test

import (
	"encoding/json"
	"labix.org/v2/mgo/bson"
	"strings"
	"testing"

	gc "launchpad.net/gocheck"
	"launchpad.net/juju-core/version"
)

type suite struct{}

var _ = gc.Suite(suite{})

func Test(t *testing.T) {
	gc.TestingT(t)
}

// N.B. The FORCE-VERSION logic is tested in the environs package.

var cmpTests = []struct {
	v1, v2 string
	compare int
}{
	{"1.0.0", "1.0.0", 0},
	{"01.0.0", "1.0.0", 0},
	{"10.0.0", "9.0.0", 1},
	{"1.0.0", "1.0.1", -1},
	{"1.0.1", "1.0.0", 1},
	{"1.0.0", "1.1.0", -1},
	{"1.1.0", "1.0.0", 1},
	{"1.0.0", "2.0.0", -1},
	{"2.0.0", "1.0.0", 1},
	{"2.0.0.0", "2.0.0", 0},
	{"2.0.0.0", "2.0.0.0", 0},
	{"2.0.0.1", "2.0.0.0", 1},
	{"2.0.1.10", "2.0.0.0", 1},
}

func (suite) TestCompare(c *gc.C) {
	for i, test := range cmpTests {
		c.Logf("test %d", i)
		v1, err := version.Parse(test.v1)
		c.Assert(err, gc.IsNil)
		v2, err := version.Parse(test.v2)
		c.Assert(err, gc.IsNil)
		compare := v1.Compare(v2)
		c.Check(compare, gc.Equals, test.compare)
	}
}

var parseTests = []struct {
	v      string
	err    string
	expect version.Number
	dev    bool
}{{
	v: "0.0.0",
}, {
	v:      "0.0.1",
	expect: version.Number{0, 0, 1, 0},
}, {
	v:      "0.0.2",
	expect: version.Number{0, 0, 2, 0},
}, {
	v:      "0.1.0",
	expect: version.Number{0, 1, 0, 0},
	dev:    true,
}, {
	v:      "0.2.3",
	expect: version.Number{0, 2, 3, 0},
}, {
	v:      "1.0.0",
	expect: version.Number{1, 0, 0, 0},
}, {
	v:      "10.234.3456",
	expect: version.Number{10, 234, 3456, 0},
}, {
	v:      "10.234.3456.1",
	expect: version.Number{10, 234, 3456, 1},
	dev:    true,
}, {
	v:      "10.234.3456.64",
	expect: version.Number{10, 234, 3456, 64},
	dev:    true,
}, {
	v:      "10.235.3456",
	expect: version.Number{10, 235, 3456, 0},
	dev:    true,
}, {
	v:   "1234567890.2.1",
	err: "invalid version.*",
}, {
	v:   "0.2..1",
	err: "invalid version.*",
}}

func (suite) TestParse(c *gc.C) {
	for i, test := range parseTests {
		c.Logf("test %d", i)
		got, err := version.Parse(test.v)
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, test.err)
		} else {
			c.Assert(err, gc.IsNil)
			c.Assert(got, gc.Equals, test.expect)
			c.Check(got.IsDev(), gc.Equals, test.dev)
			c.Check(got.String(), gc.Equals, test.v)
		}
	}
}

func binaryVersion(major, minor, patch, build int, series, arch string) version.Binary {
	return version.Binary{
		Number: version.Number{
			Major: major,
			Minor: minor,
			Patch: patch,
			Build: build,
		},
		Series: series,
		Arch:   arch,
	}
}

var parseBinaryTests = []struct {
	v      string
	err    string
	expect version.Binary
}{{
	v:      "1.2.3-a-b",
	expect: binaryVersion(1, 2, 3, 0, "a", "b"),
}, {
	v:      "1.2.3.4-a-b",
	expect: binaryVersion(1, 2, 3, 4, "a", "b"),
}, {
	v:   "1.2.3--b",
	err: "invalid binary version.*",
}, {
	v:   "1.2.3-a-",
	err: "invalid binary version.*",
}}

func (suite) TestParseBinary(c *gc.C) {
	for i, test := range parseBinaryTests {
		c.Logf("test 1: %d", i)
		got, err := version.ParseBinary(test.v)
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, test.err)
		} else {
			c.Assert(err, gc.IsNil)
			c.Assert(got, gc.Equals, test.expect)
		}
	}

	for i, test := range parseTests {
		c.Logf("test 2: %d", i)
		v := test.v + "-a-b"
		got, err := version.ParseBinary(v)
		expect := version.Binary{
			Number: test.expect,
			Series: "a",
			Arch:   "b",
		}
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, strings.Replace(test.err, "version", "binary version", 1))
		} else {
			c.Assert(err, gc.IsNil)
			c.Assert(got, gc.Equals, expect)
			c.Check(got.IsDev(), gc.Equals, test.dev)
		}
	}
}

var marshallers = []struct {
	name      string
	marshal   func(interface{}) ([]byte, error)
	unmarshal func([]byte, interface{}) error
}{{
	"json",
	json.Marshal,
	json.Unmarshal,
}, {
	"bson",
	bson.Marshal,
	bson.Unmarshal,
}}

func (suite) TestBinaryMarshalUnmarshal(c *gc.C) {
	for _, m := range marshallers {
		c.Logf("encoding %v", m.name)
		type doc struct {
			Version version.Binary
		}
		v := doc{version.MustParseBinary("1.2.3-foo-bar")}
		data, err := m.marshal(v)
		c.Assert(err, gc.IsNil)
		var nv doc
		err = m.unmarshal(data, &nv)
		c.Assert(err, gc.IsNil)
		c.Assert(v, gc.Equals, nv)
	}
}

func (suite) TestNumberMarshalUnmarshal(c *gc.C) {
	for _, m := range marshallers {
		c.Logf("encoding %v", m.name)
		type doc struct {
			Version version.Number
		}
		v := doc{version.MustParse("1.2.3")}
		data, err := m.marshal(&v)
		c.Assert(err, gc.IsNil)
		var nv doc
		err = m.unmarshal(data, &nv)
		c.Assert(err, gc.IsNil)
		c.Assert(v, gc.Equals, nv)
	}
}

var parseMajorMinorTests = []struct {
	v           string
	err         string
	expectMajor int
	expectMinor int
}{{
	v:           "1.2",
	expectMajor: 1,
	expectMinor: 2,
}, {
	v:           "1",
	expectMajor: 1,
	expectMinor: -1,
}, {
	v:   "1.2.3",
	err: "invalid major.minor version number 1.2.3",
}, {
	v:   "blah",
	err: `invalid major version number blah: strconv.ParseInt: parsing "blah": invalid syntax`,
}}

func (suite) TestParseMajorMinor(c *gc.C) {
	for i, test := range parseMajorMinorTests {
		c.Logf("test %d", i)
		major, minor, err := version.ParseMajorMinor(test.v)
		if test.err != "" {
			c.Check(err, gc.ErrorMatches, test.err)
		} else {
			c.Check(err, gc.IsNil)
			c.Check(major, gc.Equals, test.expectMajor)
			c.Check(minor, gc.Equals, test.expectMinor)
		}
	}
}
<commit_msg>go fmt<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package version_test

import (
	"encoding/json"
	"labix.org/v2/mgo/bson"
	"strings"
	"testing"

	gc "launchpad.net/gocheck"
	"launchpad.net/juju-core/version"
)

type suite struct{}

var _ = gc.Suite(suite{})

func Test(t *testing.T) {
	gc.TestingT(t)
}

// N.B. The FORCE-VERSION logic is tested in the environs package.

var cmpTests = []struct {
	v1, v2  string
	compare int
}{
	{"1.0.0", "1.0.0", 0},
	{"01.0.0", "1.0.0", 0},
	{"10.0.0", "9.0.0", 1},
	{"1.0.0", "1.0.1", -1},
	{"1.0.1", "1.0.0", 1},
	{"1.0.0", "1.1.0", -1},
	{"1.1.0", "1.0.0", 1},
	{"1.0.0", "2.0.0", -1},
	{"2.0.0", "1.0.0", 1},
	{"2.0.0.0", "2.0.0", 0},
	{"2.0.0.0", "2.0.0.0", 0},
	{"2.0.0.1", "2.0.0.0", 1},
	{"2.0.1.10", "2.0.0.0", 1},
}

func (suite) TestCompare(c *gc.C) {
	for i, test := range cmpTests {
		c.Logf("test %d", i)
		v1, err := version.Parse(test.v1)
		c.Assert(err, gc.IsNil)
		v2, err := version.Parse(test.v2)
		c.Assert(err, gc.IsNil)
		compare := v1.Compare(v2)
		c.Check(compare, gc.Equals, test.compare)
	}
}

var parseTests = []struct {
	v      string
	err    string
	expect version.Number
	dev    bool
}{{
	v: "0.0.0",
}, {
	v:      "0.0.1",
	expect: version.Number{0, 0, 1, 0},
}, {
	v:      "0.0.2",
	expect: version.Number{0, 0, 2, 0},
}, {
	v:      "0.1.0",
	expect: version.Number{0, 1, 0, 0},
	dev:    true,
}, {
	v:      "0.2.3",
	expect: version.Number{0, 2, 3, 0},
}, {
	v:      "1.0.0",
	expect: version.Number{1, 0, 0, 0},
}, {
	v:      "10.234.3456",
	expect: version.Number{10, 234, 3456, 0},
}, {
	v:      "10.234.3456.1",
	expect: version.Number{10, 234, 3456, 1},
	dev:    true,
}, {
	v:      "10.234.3456.64",
	expect: version.Number{10, 234, 3456, 64},
	dev:    true,
}, {
	v:      "10.235.3456",
	expect: version.Number{10, 235, 3456, 0},
	dev:    true,
}, {
	v:   "1234567890.2.1",
	err: "invalid version.*",
}, {
	v:   "0.2..1",
	err: "invalid version.*",
}}

func (suite) TestParse(c *gc.C) {
	for i, test := range parseTests {
		c.Logf("test %d", i)
		got, err := version.Parse(test.v)
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, test.err)
		} else {
			c.Assert(err, gc.IsNil)
			c.Assert(got, gc.Equals, test.expect)
			c.Check(got.IsDev(), gc.Equals, test.dev)
			c.Check(got.String(), gc.Equals, test.v)
		}
	}
}

func binaryVersion(major, minor, patch, build int, series, arch string) version.Binary {
	return version.Binary{
		Number: version.Number{
			Major: major,
			Minor: minor,
			Patch: patch,
			Build: build,
		},
		Series: series,
		Arch:   arch,
	}
}

var parseBinaryTests = []struct {
	v      string
	err    string
	expect version.Binary
}{{
	v:      "1.2.3-a-b",
	expect: binaryVersion(1, 2, 3, 0, "a", "b"),
}, {
	v:      "1.2.3.4-a-b",
	expect: binaryVersion(1, 2, 3, 4, "a", "b"),
}, {
	v:   "1.2.3--b",
	err: "invalid binary version.*",
}, {
	v:   "1.2.3-a-",
	err: "invalid binary version.*",
}}

func (suite) TestParseBinary(c *gc.C) {
	for i, test := range parseBinaryTests {
		c.Logf("test 1: %d", i)
		got, err := version.ParseBinary(test.v)
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, test.err)
		} else {
			c.Assert(err, gc.IsNil)
			c.Assert(got, gc.Equals, test.expect)
		}
	}

	for i, test := range parseTests {
		c.Logf("test 2: %d", i)
		v := test.v + "-a-b"
		got, err := version.ParseBinary(v)
		expect := version.Binary{
			Number: test.expect,
			Series: "a",
			Arch:   "b",
		}
		if test.err != "" {
			c.Assert(err, gc.ErrorMatches, strings.Replace(test.err, "version", "binary version", 1))
		} else {
			c.Assert(err, gc.IsNil)
			c.Assert(got, gc.Equals, expect)
			c.Check(got.IsDev(), gc.Equals, test.dev)
		}
	}
}

var marshallers = []struct {
	name      string
	marshal   func(interface{}) ([]byte, error)
	unmarshal func([]byte, interface{}) error
}{{
	"json",
	json.Marshal,
	json.Unmarshal,
}, {
	"bson",
	bson.Marshal,
	bson.Unmarshal,
}}

func (suite) TestBinaryMarshalUnmarshal(c *gc.C) {
	for _, m := range marshallers {
		c.Logf("encoding %v", m.name)
		type doc struct {
			Version version.Binary
		}
		v := doc{version.MustParseBinary("1.2.3-foo-bar")}
		data, err := m.marshal(v)
		c.Assert(err, gc.IsNil)
		var nv doc
		err = m.unmarshal(data, &nv)
		c.Assert(err, gc.IsNil)
		c.Assert(v, gc.Equals, nv)
	}
}

func (suite) TestNumberMarshalUnmarshal(c *gc.C) {
	for _, m := range marshallers {
		c.Logf("encoding %v", m.name)
		type doc struct {
			Version version.Number
		}
		v := doc{version.MustParse("1.2.3")}
		data, err := m.marshal(&v)
		c.Assert(err, gc.IsNil)
		var nv doc
		err = m.unmarshal(data, &nv)
		c.Assert(err, gc.IsNil)
		c.Assert(v, gc.Equals, nv)
	}
}

var parseMajorMinorTests = []struct {
	v           string
	err         string
	expectMajor int
	expectMinor int
}{{
	v:           "1.2",
	expectMajor: 1,
	expectMinor: 2,
}, {
	v:           "1",
	expectMajor: 1,
	expectMinor: -1,
}, {
	v:   "1.2.3",
	err: "invalid major.minor version number 1.2.3",
}, {
	v:   "blah",
	err: `invalid major version number blah: strconv.ParseInt: parsing "blah": invalid syntax`,
}}

func (suite) TestParseMajorMinor(c *gc.C) {
	for i, test := range parseMajorMinorTests {
		c.Logf("test %d", i)
		major, minor, err := version.ParseMajorMinor(test.v)
		if test.err != "" {
			c.Check(err, gc.ErrorMatches, test.err)
		} else {
			c.Check(err, gc.IsNil)
			c.Check(major, gc.Equals, test.expectMajor)
			c.Check(minor, gc.Equals, test.expectMinor)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"testing"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestParsingVersion(c *C) {
	Version, err := ParseVersion("1.5.9")
	c.Assert(err, Equals, nil)
	c.Assert(Version.Major, Equals, 1)
	c.Assert(Version.Minor, Equals, 5)
	c.Assert(Version.Patch, Equals, 9)
}

func (s *MySuite) TestParsingErrorForIncorrectNumberOfDotCharacters(c *C) {
	_, err := ParseVersion("1.5.9.9")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")

	_, err = ParseVersion("0.")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")
}

func (s *MySuite) TestParsingErrorForNonIntegerVersion(c *C) {
	_, err := ParseVersion("a.9.0")
	c.Assert(err, ErrorMatches, `Error parsing major Version a to integer. strconv.ParseInt: parsing "a": invalid syntax`)

	_, err = ParseVersion("0.ffhj.78")
	c.Assert(err, ErrorMatches, `Error parsing minor Version ffhj to integer. strconv.ParseInt: parsing "ffhj": invalid syntax`)

	_, err = ParseVersion("8.9.opl")
	c.Assert(err, ErrorMatches, `Error parsing patch Version opl to integer. strconv.ParseInt: parsing "opl": invalid syntax`)
}

func (s *MySuite) TestVersionComparisonGreaterLesser(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("4.7.2")
	version2, _ := ParseVersion("4.7.2")
	c.Assert(version1.IsEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonGreaterThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(higherVersion.IsGreaterThanEqualTo(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsGreaterThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonLesserThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("5.8.2")
	lowerVersion, _ = ParseVersion("2.9.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsLesserThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionIsBetweenTwoVersions(c *C) {
	higherVersion, _ := ParseVersion("0.0.9")
	lowerVersion, _ := ParseVersion("0.0.7")
	middleVersion, _ := ParseVersion("0.0.8")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	middleVersion, _ = ParseVersion("0.6.9")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("4.0.1")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.0.2")
	lowerVersion, _ = ParseVersion("0.0.1")
	middleVersion, _ = ParseVersion("0.0.2")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)
}

func (s *MySuite) TestGetLatestVersion(c *C) {
	highestVersion := &Version{2, 2, 2}
	versions := []*Version{&Version{0, 0, 1}, &Version{1, 2, 2}, highestVersion, &Version{0, 0, 2}, &Version{0, 2, 2}, &Version{0, 0, 3}, &Version{0, 2, 1}, &Version{0, 1, 2}}
	latestVersion := GetLatestVersion(versions)

	c.Assert(latestVersion, DeepEquals, highestVersion)
}

func (s *MySuite) TestCheckVersionCompatibilitySuccess(c *C) {
	versionSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{0, 6, 7}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.0.1"}
	gaugeVersion = &Version{1, 5, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 5, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

}

func (s *MySuite) TestCheckVersionCompatibilityFailure(c *C) {
	versionsSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{1, 9, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "1.3.1"}
	gaugeVersion = &Version{1, 3, 0}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 0, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

}

func (s *MySuite) TestFullVersionWithBuildMetadata(c *C) {
	c.Assert(FullVersion(), Equals, CurrentGaugeVersion.String())
	BuildMetadata = "nightly-2016-02-21"
	c.Assert(FullVersion(), Equals, fmt.Sprintf("%s.%s", CurrentGaugeVersion.String(), BuildMetadata))
}
<commit_msg>Fix unit tests<commit_after>// Copyright 2015 ThoughtWorks, Inc.

// This file is part of Gauge.

// Gauge is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either Version 3 of the License, or
// (at your option) any later Version.

// Gauge is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Gauge.  If not, see <http://www.gnu.org/licenses/>.

package version

import (
	"fmt"
	"testing"

	. "gopkg.in/check.v1"
)

func Test(t *testing.T) { TestingT(t) }

type MySuite struct{}

var _ = Suite(&MySuite{})

func (s *MySuite) TestParsingVersion(c *C) {
	Version, err := ParseVersion("1.5.9")
	c.Assert(err, Equals, nil)
	c.Assert(Version.Major, Equals, 1)
	c.Assert(Version.Minor, Equals, 5)
	c.Assert(Version.Patch, Equals, 9)
}

func (s *MySuite) TestParsingErrorForIncorrectNumberOfDotCharacters(c *C) {
	_, err := ParseVersion("1.5.9.9")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")

	_, err = ParseVersion("0.")
	c.Assert(err, ErrorMatches, "Incorrect Version format. Version should be in the form 1.5.7")
}

func (s *MySuite) TestParsingErrorForNonIntegerVersion(c *C) {
	_, err := ParseVersion("a.9.0")
	c.Assert(err, ErrorMatches, `Error parsing major Version a to integer. strconv.Atoi: parsing "a": invalid syntax`)

	_, err = ParseVersion("0.ffhj.78")
	c.Assert(err, ErrorMatches, `Error parsing minor Version ffhj to integer. strconv.Atoi: parsing "ffhj": invalid syntax`)

	_, err = ParseVersion("8.9.opl")
	c.Assert(err, ErrorMatches, `Error parsing patch Version opl to integer. strconv.Atoi: parsing "opl": invalid syntax`)
}

func (s *MySuite) TestVersionComparisonGreaterLesser(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("4.7.2")
	version2, _ := ParseVersion("4.7.2")
	c.Assert(version1.IsEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonGreaterThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(higherVersion.IsGreaterThanEqualTo(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	c.Assert(lowerVersion.IsLesserThan(higherVersion), Equals, true)
	c.Assert(higherVersion.IsGreaterThan(lowerVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsGreaterThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionComparisonLesserThanEqual(c *C) {
	higherVersion, _ := ParseVersion("0.0.7")
	lowerVersion, _ := ParseVersion("0.0.3")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("5.8.2")
	lowerVersion, _ = ParseVersion("2.9.7")
	c.Assert(lowerVersion.IsLesserThanEqualTo(higherVersion), Equals, true)

	version1, _ := ParseVersion("6.7.2")
	version2, _ := ParseVersion("6.7.2")
	c.Assert(version1.IsLesserThanEqualTo(version2), Equals, true)
}

func (s *MySuite) TestVersionIsBetweenTwoVersions(c *C) {
	higherVersion, _ := ParseVersion("0.0.9")
	lowerVersion, _ := ParseVersion("0.0.7")
	middleVersion, _ := ParseVersion("0.0.8")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.7.2")
	lowerVersion, _ = ParseVersion("0.5.7")
	middleVersion, _ = ParseVersion("0.6.9")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("3.8.7")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("4.7.2")
	lowerVersion, _ = ParseVersion("4.0.1")
	middleVersion, _ = ParseVersion("4.0.1")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)

	higherVersion, _ = ParseVersion("0.0.2")
	lowerVersion, _ = ParseVersion("0.0.1")
	middleVersion, _ = ParseVersion("0.0.2")
	c.Assert(middleVersion.IsBetween(lowerVersion, higherVersion), Equals, true)
}

func (s *MySuite) TestGetLatestVersion(c *C) {
	highestVersion := &Version{2, 2, 2}
	versions := []*Version{&Version{0, 0, 1}, &Version{1, 2, 2}, highestVersion, &Version{0, 0, 2}, &Version{0, 2, 2}, &Version{0, 0, 3}, &Version{0, 2, 1}, &Version{0, 1, 2}}
	latestVersion := GetLatestVersion(versions)

	c.Assert(latestVersion, DeepEquals, highestVersion)
}

func (s *MySuite) TestCheckVersionCompatibilitySuccess(c *C) {
	versionSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{0, 6, 7}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.0.1"}
	gaugeVersion = &Version{1, 5, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

	versionSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 5, 1}
	c.Assert(CheckCompatibility(gaugeVersion, versionSupported), Equals, nil)

}

func (s *MySuite) TestCheckVersionCompatibilityFailure(c *C) {
	versionsSupported := &VersionSupport{"0.6.5", "1.8.5"}
	gaugeVersion := &Version{1, 9, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{"0.0.1", "0.0.1"}
	gaugeVersion = &Version{0, 0, 2}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "1.3.1"}
	gaugeVersion = &Version{1, 3, 0}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

	versionsSupported = &VersionSupport{Minimum: "0.5.1"}
	gaugeVersion = &Version{0, 0, 9}
	c.Assert(CheckCompatibility(gaugeVersion, versionsSupported), NotNil)

}

func (s *MySuite) TestFullVersionWithBuildMetadata(c *C) {
	c.Assert(FullVersion(), Equals, CurrentGaugeVersion.String())
	BuildMetadata = "nightly-2016-02-21"
	c.Assert(FullVersion(), Equals, fmt.Sprintf("%s.%s", CurrentGaugeVersion.String(), BuildMetadata))
}
<|endoftext|>


<commit_before>package metadata

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
)

func TestMetadataGenerator(t *testing.T) {
	tests := []struct {
		command  string
		metadata string
		err      bool
	}{
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata "{}"`,
			metadata: `{}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 1 -metadata "{}"`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": "metadata", "foo": [100, 200, {}, null]}'`,
			metadata: `{"example":"metadata","foo":[100,200,{},null]}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": metadata"}'`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '"foobar"'`,
			metadata: `"foobar"`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata foobar`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata 262144`,
			metadata: `262144`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata true`,
			metadata: `true`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata null`,
			metadata: `null`,
			err:      false,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				Command: test.command,
			},
		}
		metadata, err := g.Fetch()
		if err != nil {
			if !test.err {
				t.Errorf("error occurred unexpectedly on command %q", test.command)
			}
		} else {
			if test.err {
				t.Errorf("error did not occurr but error expected on command %q", test.command)
			}
			metadataStr, _ := json.Marshal(metadata)
			if string(metadataStr) != test.metadata {
				t.Errorf("metadata should be %q but got %q", test.metadata, string(metadataStr))
			}
		}
	}
}

func TestMetadataGeneratorSaveDiffers(t *testing.T) {
	tests := []struct {
		prevmetadata string
		metadata     string
		differs      bool
	}{
		{
			prevmetadata: `{}`,
			metadata:     `{}`,
			differs:      false,
		},
		{
			prevmetadata: `{ "foo": [ 100, 200, null, {} ] }`,
			metadata:     `{"foo":[100,200,null,{}]}`,
			differs:      false,
		},
		{
			prevmetadata: `null`,
			metadata:     `{}`,
			differs:      true,
		},
		{
			prevmetadata: `[]`,
			metadata:     `{}`,
			differs:      true,
		},
	}
	for _, test := range tests {
		g := Generator{}
		var prevmetadata interface{}
		_ = json.Unmarshal([]byte(test.prevmetadata), &prevmetadata)
		g.Save(prevmetadata)

		var metadata interface{}
		_ = json.Unmarshal([]byte(test.metadata), &metadata)

		got := g.Differs(metadata)
		if got != test.differs {
			t.Errorf("Differs() should return %t but got %t for %v, %v", test.differs, got, prevmetadata, metadata)
		}
	}
}

func TestMetadataGeneratorInterval(t *testing.T) {
	tests := []struct {
		interval *int32
		expected time.Duration
	}{
		{
			interval: pint(0),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(1),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(30),
			expected: 30 * time.Minute,
		},
		{
			interval: nil,
			expected: 10 * time.Minute,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				ExecutionInterval: test.interval,
			},
		}
		if g.Interval() != test.expected {
			t.Errorf("interval should be %v but got: %v", test.expected, g.Interval())
		}
	}
}

func pint(i int32) *int32 {
	return &i
}
<commit_msg>rename test name<commit_after>package metadata

import (
	"encoding/json"
	"testing"
	"time"

	"github.com/mackerelio/mackerel-agent/config"
)

func TestMetadataGeneratorFetch(t *testing.T) {
	tests := []struct {
		command  string
		metadata string
		err      bool
	}{
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata "{}"`,
			metadata: `{}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 1 -metadata "{}"`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": "metadata", "foo": [100, 200, {}, null]}'`,
			metadata: `{"example":"metadata","foo":[100,200,{},null]}`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '{"example": metadata"}'`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata '"foobar"'`,
			metadata: `"foobar"`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata foobar`,
			metadata: ``,
			err:      true,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata 262144`,
			metadata: `262144`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata true`,
			metadata: `true`,
			err:      false,
		},
		{
			command:  `go run testdata/json.go -exit-code 0 -metadata null`,
			metadata: `null`,
			err:      false,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				Command: test.command,
			},
		}
		metadata, err := g.Fetch()
		if err != nil {
			if !test.err {
				t.Errorf("error occurred unexpectedly on command %q", test.command)
			}
		} else {
			if test.err {
				t.Errorf("error did not occurr but error expected on command %q", test.command)
			}
			metadataStr, _ := json.Marshal(metadata)
			if string(metadataStr) != test.metadata {
				t.Errorf("metadata should be %q but got %q", test.metadata, string(metadataStr))
			}
		}
	}
}

func TestMetadataGeneratorSaveDiffers(t *testing.T) {
	tests := []struct {
		prevmetadata string
		metadata     string
		differs      bool
	}{
		{
			prevmetadata: `{}`,
			metadata:     `{}`,
			differs:      false,
		},
		{
			prevmetadata: `{ "foo": [ 100, 200, null, {} ] }`,
			metadata:     `{"foo":[100,200,null,{}]}`,
			differs:      false,
		},
		{
			prevmetadata: `null`,
			metadata:     `{}`,
			differs:      true,
		},
		{
			prevmetadata: `[]`,
			metadata:     `{}`,
			differs:      true,
		},
	}
	for _, test := range tests {
		g := Generator{}
		var prevmetadata interface{}
		_ = json.Unmarshal([]byte(test.prevmetadata), &prevmetadata)
		g.Save(prevmetadata)

		var metadata interface{}
		_ = json.Unmarshal([]byte(test.metadata), &metadata)

		got := g.Differs(metadata)
		if got != test.differs {
			t.Errorf("Differs() should return %t but got %t for %v, %v", test.differs, got, prevmetadata, metadata)
		}
	}
}

func TestMetadataGeneratorInterval(t *testing.T) {
	tests := []struct {
		interval *int32
		expected time.Duration
	}{
		{
			interval: pint(0),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(1),
			expected: 1 * time.Minute,
		},
		{
			interval: pint(30),
			expected: 30 * time.Minute,
		},
		{
			interval: nil,
			expected: 10 * time.Minute,
		},
	}
	for _, test := range tests {
		g := Generator{
			Config: &config.MetadataPlugin{
				ExecutionInterval: test.interval,
			},
		}
		if g.Interval() != test.expected {
			t.Errorf("interval should be %v but got: %v", test.expected, g.Interval())
		}
	}
}

func pint(i int32) *int32 {
	return &i
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"go-daemon"
	"log"
	"os"
	"syscall"
	"time"
)

const (
	pidFileName = "dmn.pid"
	logFileName = "dmn.log"

	fileMask = 0600
)
const (
	ret_OK = iota
	ret_ALREADYRUN
	ret_PIDFERROR
	ret_REBORNERROR
	ret_CONFERROR
)

var (
	status = flag.Bool("status", false,
		`Check status of the daemon. The program immediately exits after these
		checks with either a return code of 0 (Daemon Stopped) or return code
		not equal to 0 (Daemon Running)`)

	silent = flag.Bool("silent", false, "Don't write in stdout")

	test = flag.Bool("t", false,
		`Run syntax tests for configuration files only. The program
		immediately exits after these syntax parsing tests with either
		a return code of 0 (Syntax OK) or return code not equal to 0
		(Syntax Error)`)

	configFileName = flag.String("f", "dmn.conf",
		`Specifies the name of the configuration file. The default is dmn.conf.
		Daemon refuses to start if there is no configuration file.`)
)

var confProv = make(chan Config, 8)

func main() {
	flag.Parse()

	setupLogging()

	conf, err := loadConfig(*configFileName)
	if err != nil {
		log.Println("Config error:", err)
		os.Exit(ret_CONFERROR)
	}
	if *test {
		os.Exit(ret_OK)
	}

	pidf := lockPidFile()
	err = daemon.Reborn(027, "./")
	if err != nil {
		log.Println("Reborn error:", err)
		os.Exit(ret_REBORNERROR)
	}

	confProv <- conf
	go watchdog(confProv)

	serveSignals()

	pidf.Unlock()
}

func setupLogging() {
	if daemon.WasReborn() {
		file, _ := os.OpenFile(logFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, fileMask)
		daemon.RedirectStream(os.Stdout, file)
		daemon.RedirectStream(os.Stderr, file)
		file.Close()
		log.Println("--- log ---")
	} else {
		log.SetFlags(0)
		if *silent {
			file, _ := os.OpenFile(os.DevNull, os.O_WRONLY, fileMask)
			daemon.RedirectStream(os.Stdout, file)
			daemon.RedirectStream(os.Stderr, file)
			file.Close()
		}
	}
}

type Config []string

func loadConfig(path string) (config Config, err error) {
	var file *os.File
	file, err = os.OpenFile(path, os.O_RDONLY, 0700)
	if err != nil {
		return
	}
	defer file.Close()

	config = make([]string, 0)
	err = json.NewDecoder(file).Decode(&config)
	if err != nil {
		return
	}
	for _, path = range config {
		if _, err = os.Stat(path); os.IsNotExist(err) {
			return
		}
	}

	return
}

func lockPidFile() *daemon.PidFile {
	pidf, err := daemon.LockPidFile(pidFileName, fileMask)
	if err != nil {
		if err == daemon.ErrWouldBlock {
			log.Println("daemon copy is already running")
			os.Exit(ret_ALREADYRUN)
		} else {
			log.Println("pid file creation error:", err)
			os.Exit(ret_PIDFERROR)
		}
	}

	if !daemon.WasReborn() {
		pidf.Unlock()
	}

	if *status {
		os.Exit(ret_OK)
	}

	return pidf
}

func watchdog(confProv <-chan Config) {
	states := make(map[string]time.Time)
	conf := <-confProv
	for {
		select {
		case conf = <-confProv:
		default:
		}

		for _, path := range conf {
			fi, err := os.Stat(path)
			if err != nil {
				log.Println(err)
				continue
			}

			cur := fi.ModTime()
			if pre, exists := states[path]; exists {
				if pre != cur {
					log.Printf("file %s modified at %s", path, cur)
				}
			}
			states[path] = cur
		}
		time.Sleep(time.Second)
	}
}

func serveSignals() {
	daemon.SetHandler(termHandler, syscall.SIGTERM, syscall.SIGKILL)
	daemon.SetHandler(hupHandler, syscall.SIGHUP)

	err := daemon.ServeSignals()
	if err != nil {
		log.Println("Error:", err)
	}

	log.Println("--- end ---")
}

func termHandler(sig os.Signal) error {
	log.Println("SIGTERM:", sig)
	return daemon.ErrStop
}

func hupHandler(sig os.Signal) error {
	log.Println("SIGHUP:", sig)

	conf, err := loadConfig(*configFileName)
	if err != nil {
		log.Println("Config error:", err)
	} else {
		confProv <- conf
	}

	return nil
}
<commit_msg>Update dmn.go<commit_after>package main

import (
	"encoding/json"
	"flag"
	"github.com/sevlyar/go-daemon"
	"log"
	"os"
	"syscall"
	"time"
)

const (
	pidFileName = "dmn.pid"
	logFileName = "dmn.log"

	fileMask = 0600
)
const (
	ret_OK = iota
	ret_ALREADYRUN
	ret_PIDFERROR
	ret_REBORNERROR
	ret_CONFERROR
)

var (
	status = flag.Bool("status", false,
		`Check status of the daemon. The program immediately exits after these
		checks with either a return code of 0 (Daemon Stopped) or return code
		not equal to 0 (Daemon Running)`)

	silent = flag.Bool("silent", false, "Don't write in stdout")

	test = flag.Bool("t", false,
		`Run syntax tests for configuration files only. The program
		immediately exits after these syntax parsing tests with either
		a return code of 0 (Syntax OK) or return code not equal to 0
		(Syntax Error)`)

	configFileName = flag.String("f", "dmn.conf",
		`Specifies the name of the configuration file. The default is dmn.conf.
		Daemon refuses to start if there is no configuration file.`)
)

var confProv = make(chan Config, 8)

func main() {
	flag.Parse()

	setupLogging()

	conf, err := loadConfig(*configFileName)
	if err != nil {
		log.Println("Config error:", err)
		os.Exit(ret_CONFERROR)
	}
	if *test {
		os.Exit(ret_OK)
	}

	pidf := lockPidFile()
	err = daemon.Reborn(027, "./")
	if err != nil {
		log.Println("Reborn error:", err)
		os.Exit(ret_REBORNERROR)
	}

	confProv <- conf
	go watchdog(confProv)

	serveSignals()

	pidf.Unlock()
}

func setupLogging() {
	if daemon.WasReborn() {
		file, _ := os.OpenFile(logFileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, fileMask)
		daemon.RedirectStream(os.Stdout, file)
		daemon.RedirectStream(os.Stderr, file)
		file.Close()
		log.Println("--- log ---")
	} else {
		log.SetFlags(0)
		if *silent {
			file, _ := os.OpenFile(os.DevNull, os.O_WRONLY, fileMask)
			daemon.RedirectStream(os.Stdout, file)
			daemon.RedirectStream(os.Stderr, file)
			file.Close()
		}
	}
}

type Config []string

func loadConfig(path string) (config Config, err error) {
	var file *os.File
	file, err = os.OpenFile(path, os.O_RDONLY, 0700)
	if err != nil {
		return
	}
	defer file.Close()

	config = make([]string, 0)
	err = json.NewDecoder(file).Decode(&config)
	if err != nil {
		return
	}
	for _, path = range config {
		if _, err = os.Stat(path); os.IsNotExist(err) {
			return
		}
	}

	return
}

func lockPidFile() *daemon.PidFile {
	pidf, err := daemon.LockPidFile(pidFileName, fileMask)
	if err != nil {
		if err == daemon.ErrWouldBlock {
			log.Println("daemon copy is already running")
			os.Exit(ret_ALREADYRUN)
		} else {
			log.Println("pid file creation error:", err)
			os.Exit(ret_PIDFERROR)
		}
	}

	if !daemon.WasReborn() {
		pidf.Unlock()
	}

	if *status {
		os.Exit(ret_OK)
	}

	return pidf
}

func watchdog(confProv <-chan Config) {
	states := make(map[string]time.Time)
	conf := <-confProv
	for {
		select {
		case conf = <-confProv:
		default:
		}

		for _, path := range conf {
			fi, err := os.Stat(path)
			if err != nil {
				log.Println(err)
				continue
			}

			cur := fi.ModTime()
			if pre, exists := states[path]; exists {
				if pre != cur {
					log.Printf("file %s modified at %s", path, cur)
				}
			}
			states[path] = cur
		}
		time.Sleep(time.Second)
	}
}

func serveSignals() {
	daemon.SetHandler(termHandler, syscall.SIGTERM, syscall.SIGKILL)
	daemon.SetHandler(hupHandler, syscall.SIGHUP)

	err := daemon.ServeSignals()
	if err != nil {
		log.Println("Error:", err)
	}

	log.Println("--- end ---")
}

func termHandler(sig os.Signal) error {
	log.Println("SIGTERM:", sig)
	return daemon.ErrStop
}

func hupHandler(sig os.Signal) error {
	log.Println("SIGHUP:", sig)

	conf, err := loadConfig(*configFileName)
	if err != nil {
		log.Println("Config error:", err)
	} else {
		confProv <- conf
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Lars Wiegman. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

package mock

// UserService represents a mock implementation of multipass.UserService.
type UserService struct {
	RegisterFn      func(handle string) error
	RegisterInvoked bool

	ListedFn      func(handle string) error
	ListedInvoked bool

	AuthorizedFn      func(handle, rawurl string) bool
	AuthorizedInvoked bool

	Notify        func(handle, loginurl string) error
	NotifyInvoked bool

	CloseFn      func() error
	CloseInvoked bool
}

// Register invokes the mock implementation and marks the function as invoked.
func (s *UserService) Register(handle string) error {
	s.RegisterInvoked = true
	return s.RegisterFn(handle)
}

// Listed invokes the mock implementation and marks the function as invoked.
func (s *UserService) Listed(handle string) bool {
	s.ListedInvoked = true
	return s.ListedFn(handle)
}

// Notify invokes the mock implementation and marks the function as invoked.
func (s *UserService) Notify(handle, loginurl string) error {
	s.NotifyInvoked = true
	return s.NotifyFn(handle)
}

// Authorized invokes the mock implementation and marks the function as invoked.
func (s *UserService) Authorized(handle, rawurl string) bool {
	s.AuthorizedInvoked = true
	return s.AuthorizedFn(handle, rawurl)
}

// Close invokes the mock implementation and marks the function as invoked.
func (s *UserService) Close() error {
	s.CloseInvoked = true
	return s.CloseFn(handle)
}
<commit_msg>Fix methods of mock.UserService<commit_after>// Copyright 2016 Lars Wiegman. All rights reserved. Use of this source code is
// governed by a BSD-style license that can be found in the LICENSE file.

package mock

// UserService represents a mock implementation of multipass.UserService.
type UserService struct {
	RegisterFn      func(handle string) error
	RegisterInvoked bool

	ListedFn      func(handle string) bool
	ListedInvoked bool

	AuthorizedFn      func(handle, rawurl string) bool
	AuthorizedInvoked bool

	NotifyFn      func(handle, loginurl string) error
	NotifyInvoked bool

	CloseFn      func() error
	CloseInvoked bool
}

// Register invokes the mock implementation and marks the function as invoked.
func (s *UserService) Register(handle string) error {
	s.RegisterInvoked = true
	return s.RegisterFn(handle)
}

// Listed invokes the mock implementation and marks the function as invoked.
func (s *UserService) Listed(handle string) bool {
	s.ListedInvoked = true
	return s.ListedFn(handle)
}

// Notify invokes the mock implementation and marks the function as invoked.
func (s *UserService) Notify(handle, loginurl string) error {
	s.NotifyInvoked = true
	return s.NotifyFn(handle, loginurl)
}

// Authorized invokes the mock implementation and marks the function as invoked.
func (s *UserService) Authorized(handle, rawurl string) bool {
	s.AuthorizedInvoked = true
	return s.AuthorizedFn(handle, rawurl)
}

// Close invokes the mock implementation and marks the function as invoked.
func (s *UserService) Close() error {
	s.CloseInvoked = true
	return s.CloseFn()
}
<|endoftext|>


<commit_before>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"time"
)

// Board represents a Trello Board.
// https://developers.trello.com/reference/#boardsid
type Board struct {
	client         *Client
	ID             string `json:"id"`
	Name           string `json:"name"`
	Desc           string `json:"desc"`
	Closed         bool   `json:"closed"`
	IdOrganization string `json:"idOrganization"`
	Pinned         bool   `json:"pinned"`
	Url            string `json:"url"`
	ShortUrl       string `json:"shortUrl"`
	Prefs          struct {
		PermissionLevel       string            `json:"permissionLevel"`
		Voting                string            `json:"voting"`
		Comments              string            `json:"comments"`
		Invitations           string            `json:"invitations"`
		SelfJoin              bool              `json:"selfjoin"`
		CardCovers            bool              `json:"cardCovers"`
		CardAging             string            `json:"cardAging"`
		CalendarFeedEnabled   bool              `json:"calendarFeedEnabled"`
		Background            string            `json:"background"`
		BackgroundColor       string            `json:"backgroundColor"`
		BackgroundImage       string            `json:"backgroundImage"`
		BackgroundImageScaled []BackgroundImage `json:"backgroundImageScaled"`
		BackgroundTile        bool              `json:"backgroundTile"`
		BackgroundBrightness  string            `json:"backgroundBrightness"`
		CanBePublic           bool              `json:"canBePublic"`
		CanBeOrg              bool              `json:"canBeOrg"`
		CanBePrivate          bool              `json:"canBePrivate"`
		CanInvite             bool              `json:"canInvite"`
	} `json:"prefs"`
	LabelNames struct {
		Black  string `json:"black,omitempty"`
		Blue   string `json:"blue,omitempty"`
		Green  string `json:"green,omitempty"`
		Lime   string `json:"lime,omitempty"`
		Orange string `json:"orange,omitempty"`
		Pink   string `json:"pink,omitempty"`
		Purple string `json:"purple,omitempty"`
		Red    string `json:"red,omitempty"`
		Sky    string `json:"sky,omitempty"`
		Yellow string `json:"yellow,omitempty"`
	} `json:"labelNames"`
	Lists        []*List      `json:"lists"`
	Actions      []*Action    `json:"actions"`
	Organization Organization `json:"organization"`
}

// NewBoard is a constructor that sets the default values
// for Prefs.SelfJoin and Prefs.CardCovers also set by the API.
func NewBoard(name string) Board {
	b := Board{Name: name}

	// default values in line with API POST
	b.Prefs.SelfJoin = true
	b.Prefs.CardCovers = true

	return b
}

type BackgroundImage struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	URL    string `json:"url"`
}

func (b *Board) CreatedAt() time.Time {
	t, _ := IDToTime(b.ID)
	return t
}

/**
 * Board retrieves a Trello board by its ID.
 */
func (c *Client) GetBoard(boardID string, args Arguments) (board *Board, err error) {
	path := fmt.Sprintf("boards/%s", boardID)
	err = c.Get(path, args, &board)
	if board != nil {
		board.client = c
	}
	return
}

func (m *Member) GetBoards(args Arguments) (boards []*Board, err error) {
	path := fmt.Sprintf("members/%s/boards", m.ID)
	err = m.client.Get(path, args, &boards)
	for i := range boards {
		boards[i].client = m.client
	}
	return
}
<commit_msg>Add create method for Board for talking to POST endpoint<commit_after>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
	"time"
)

// Board represents a Trello Board.
// https://developers.trello.com/reference/#boardsid
type Board struct {
	client         *Client
	ID             string `json:"id"`
	Name           string `json:"name"`
	Desc           string `json:"desc"`
	Closed         bool   `json:"closed"`
	IdOrganization string `json:"idOrganization"`
	Pinned         bool   `json:"pinned"`
	Url            string `json:"url"`
	ShortUrl       string `json:"shortUrl"`
	Prefs          struct {
		PermissionLevel       string            `json:"permissionLevel"`
		Voting                string            `json:"voting"`
		Comments              string            `json:"comments"`
		Invitations           string            `json:"invitations"`
		SelfJoin              bool              `json:"selfjoin"`
		CardCovers            bool              `json:"cardCovers"`
		CardAging             string            `json:"cardAging"`
		CalendarFeedEnabled   bool              `json:"calendarFeedEnabled"`
		Background            string            `json:"background"`
		BackgroundColor       string            `json:"backgroundColor"`
		BackgroundImage       string            `json:"backgroundImage"`
		BackgroundImageScaled []BackgroundImage `json:"backgroundImageScaled"`
		BackgroundTile        bool              `json:"backgroundTile"`
		BackgroundBrightness  string            `json:"backgroundBrightness"`
		CanBePublic           bool              `json:"canBePublic"`
		CanBeOrg              bool              `json:"canBeOrg"`
		CanBePrivate          bool              `json:"canBePrivate"`
		CanInvite             bool              `json:"canInvite"`
	} `json:"prefs"`
	LabelNames struct {
		Black  string `json:"black,omitempty"`
		Blue   string `json:"blue,omitempty"`
		Green  string `json:"green,omitempty"`
		Lime   string `json:"lime,omitempty"`
		Orange string `json:"orange,omitempty"`
		Pink   string `json:"pink,omitempty"`
		Purple string `json:"purple,omitempty"`
		Red    string `json:"red,omitempty"`
		Sky    string `json:"sky,omitempty"`
		Yellow string `json:"yellow,omitempty"`
	} `json:"labelNames"`
	Lists        []*List      `json:"lists"`
	Actions      []*Action    `json:"actions"`
	Organization Organization `json:"organization"`
}

// NewBoard is a constructor that sets the default values
// for Prefs.SelfJoin and Prefs.CardCovers also set by the API.
func NewBoard(name string) Board {
	b := Board{Name: name}

	// default values in line with API POST
	b.Prefs.SelfJoin = true
	b.Prefs.CardCovers = true

	return b
}

type BackgroundImage struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	URL    string `json:"url"`
}

func (b *Board) CreatedAt() time.Time {
	t, _ := IDToTime(b.ID)
	return t
}

// CreateBoard creates a board remote.
// Attribute currently supported as exra argument: powerUps.
// Attributes currently known to be unsupported: idBoardSource, keepFromSource.
//
// API Docs: https://developers.trello.com/reference/#boardsid
func (c *Client) CreateBoard(board *Board, extraArgs Arguments) error {
	path := "boards"
	args := Arguments{
		"desc":             board.Desc,
		"name":             board.Name,
		"prefs_selfJoin":   fmt.Sprintf("%t", board.Prefs.SelfJoin),
		"prefs_cardCovers": fmt.Sprintf("%t", board.Prefs.CardCovers),
		"idOrganization":   board.IdOrganization,
	}

	if board.Prefs.Voting != "" {
		args["prefs_voting"] = board.Prefs.Voting
	}
	if board.Prefs.PermissionLevel != "" {
		args["prefs_permissionLevel"] = board.Prefs.PermissionLevel
	}
	if board.Prefs.Comments != "" {
		args["prefs_comments"] = board.Prefs.Comments
	}
	if board.Prefs.Invitations != "" {
		args["prefs_invitations"] = board.Prefs.Invitations
	}
	if board.Prefs.Background != "" {
		args["prefs_background"] = board.Prefs.Background
	}
	if board.Prefs.CardAging != "" {
		args["prefs_cardAging"] = board.Prefs.CardAging
	}

	// Expects one of "all", "calendar", "cardAging", "recap", or "voting".
	if powerUps, ok := extraArgs["powerUps"]; ok {
		args["powerUps"] = powerUps
	}

	err := c.Post(path, args, &board)
	if err == nil {
		board.client = c
	}
	return err
}
/**
 * Board retrieves a Trello board by its ID.
 */
func (c *Client) GetBoard(boardID string, args Arguments) (board *Board, err error) {
	path := fmt.Sprintf("boards/%s", boardID)
	err = c.Get(path, args, &board)
	if board != nil {
		board.client = c
	}
	return
}

func (m *Member) GetBoards(args Arguments) (boards []*Board, err error) {
	path := fmt.Sprintf("members/%s/boards", m.ID)
	err = m.client.Get(path, args, &boards)
	for i := range boards {
		boards[i].client = m.client
	}
	return
}
<|endoftext|>


<commit_before>package filelist_test

import (
	"../filelist"
	"log"
)

func main() {
	log.Fatalf("filelist_test.go not implemented")
	rootDir := "../filelist"
	dirContents, err := filelist.GetDirectoryListing(rootDir)
	if err != nil {
		log.Fatalf("GetDirectoryListing() threw err %v\n", err)
	}
	log.Printf("dirContents: %v\n", dirContents)
}
<commit_msg>Initial Sketch<commit_after>package filelist_test

import (
	"../filelist"
	"fmt"
	"testing"
)

func indexOf(target string, arrayOfStrings []string) bool {
	for _, str := range arrayOfStrings {
		if target == str {
			return true
		}
	}
	return false
}

func TestGetDirectoryListing(t *testing.T) {
	expectedDirContents := []string{
		"../filelist",
		"../filelist/filelist.go",
		"../filelist/filelist_test.go",
	}
	rootDir := "../filelist"
	dirContents, err := filelist.GetDirectoryListing(rootDir)
	if err != nil {
		t.Error("GetDirectoryListing() threw err %v\n", err)
	}
	fmt.Printf("DEBUG %v\n", dirContents)
	if len(dirContents) != 3 {
		t.Error("unexpected directory results: %v\n", dirContents)
	}

	for _, target := range expectedDirContents {
		if indexOf(target, dirContents) == false {
			t.Error("Could not find %s in %v\n", target, dirContents)
		}
	}
}
<|endoftext|>


<commit_before>// Package importer defines the Importer, which loads, parses and
// type-checks packages of Go code plus their transitive closure, and
// retains both the ASTs and the derived facts.
//
// TODO(adonovan): document and test this package better, with examples.
// Currently it's covered by the ssa/ tests.
//
package importer

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"

	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
)

// An Importer's methods are not thread-safe.
type Importer struct {
	config   *Config                 // the client configuration
	Fset     *token.FileSet          // position info for all files seen
	Packages map[string]*PackageInfo // keys are import paths
	errors   map[string]error        // cache of errors by import path
}

// Config specifies the configuration for the importer.
//
type Config struct {
	// TypeChecker contains options relating to the type checker.
	// The Importer will override any user-supplied values for its
	// Error and Import fields; other fields will be passed
	// through to the type checker.
	TypeChecker types.Config

	// If Loader is non-nil, it is used to satisfy imports.
	//
	// If it is nil, binary object files produced by the gc
	// compiler will be loaded instead of source code for all
	// imported packages.  Such files supply only the types of
	// package-level declarations and values of constants, but no
	// code, so this mode will not yield a whole program.  It is
	// intended for analyses that perform intraprocedural analysis
	// of a single package.
	Loader SourceLoader
}

// SourceLoader is the signature of a function that locates, reads and
// parses a set of source files given an import path.
//
// fset is the fileset to which the ASTs should be added.
// path is the imported path, e.g. "sync/atomic".
//
// On success, the function returns files, the set of ASTs produced,
// or the first error encountered.
//
// The MakeGoBuildLoader utility can be used to construct a
// SourceLoader based on go/build.
//
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)

// New returns a new, empty Importer using configuration options
// specified by config.
//
func New(config *Config) *Importer {
	imp := &Importer{
		config:   config,
		Fset:     token.NewFileSet(),
		Packages: make(map[string]*PackageInfo),
		errors:   make(map[string]error),
	}
	imp.config.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
	imp.config.TypeChecker.Import = imp.doImport
	return imp
}

// doImport loads the typechecker package identified by path
// Implements the types.Importer prototype.
//
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// Package unsafe is handled specially, and has no PackageInfo.
	if path == "unsafe" {
		return types.Unsafe, nil
	}

	// Load the source/binary for 'path', type-check it, construct
	// a PackageInfo and update our map (imp.Packages) and the
	// type-checker's map (imports).
	var info *PackageInfo
	if imp.config.Loader != nil {
		info, err = imp.LoadPackage(path)
	} else {
		if info, ok := imp.Packages[path]; ok {
			imports[path] = info.Pkg
			pkg = info.Pkg
			return // positive cache hit
		}

		if err = imp.errors[path]; err != nil {
			return // negative cache hit
		}

		if pkg, err = types.GcImport(imports, path); err == nil {
			info = &PackageInfo{Pkg: pkg}
			imp.Packages[path] = info
		}
	}

	if err == nil {
		// Cache success.
		pkg = info.Pkg
		imports[path] = pkg
		return pkg, nil
	}

	// Cache failure
	imp.errors[path] = err
	return nil, err
}

// LoadPackage loads the package of the specified import-path,
// performs type-checking, and returns the corresponding
// PackageInfo.
//
// Not idempotent!
// Precondition: Importer.config.Loader != nil.
// TODO(adonovan): fix: violated in call from CreatePackageFromArgs!
// Not thread-safe!
// TODO(adonovan): rethink this API.
//
func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
	if info, ok := imp.Packages[importPath]; ok {
		return info, nil // positive cache hit
	}

	if err := imp.errors[importPath]; err != nil {
		return nil, err // negative cache hit
	}

	if imp.config.Loader == nil {
		panic("Importer.LoadPackage without a SourceLoader")
	}
	files, err := imp.config.Loader(imp.Fset, importPath)
	if err != nil {
		return nil, err
	}
	info := imp.CreateSourcePackage(importPath, files)
	if info.Err != nil {
		return nil, info.Err
	}
	return info, nil
}

// CreateSourcePackage invokes the type-checker on files and returns a
// PackageInfo containing the resulting type-checker package, the
// ASTs, and other type information.
//
// The order of files determines the package initialization order.
//
// importPath is the full name under which this package is known, such
// as appears in an import declaration. e.g. "sync/atomic".
//
// The ParseFiles utility may be helpful for parsing a set of Go
// source files.
//
// The result is always non-nil; the presence of errors is indicated
// by the PackageInfo.Err field.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *PackageInfo {
	pkgInfo := &PackageInfo{
		Files: files,
		Info: types.Info{
			Types:      make(map[ast.Expr]types.Type),
			Values:     make(map[ast.Expr]exact.Value),
			Objects:    make(map[*ast.Ident]types.Object),
			Implicits:  make(map[ast.Node]types.Object),
			Selections: make(map[*ast.SelectorExpr]*types.Selection),
		},
	}
	pkgInfo.Pkg, pkgInfo.Err = imp.config.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)
	imp.Packages[importPath] = pkgInfo
	return pkgInfo
}
<commit_msg>go.tools/importer: retain scope information.<commit_after>// Package importer defines the Importer, which loads, parses and
// type-checks packages of Go code plus their transitive closure, and
// retains both the ASTs and the derived facts.
//
// TODO(adonovan): document and test this package better, with examples.
// Currently it's covered by the ssa/ tests.
//
package importer

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"

	"code.google.com/p/go.tools/go/exact"
	"code.google.com/p/go.tools/go/types"
)

// An Importer's methods are not thread-safe.
type Importer struct {
	config   *Config                 // the client configuration
	Fset     *token.FileSet          // position info for all files seen
	Packages map[string]*PackageInfo // keys are import paths
	errors   map[string]error        // cache of errors by import path
}

// Config specifies the configuration for the importer.
//
type Config struct {
	// TypeChecker contains options relating to the type checker.
	// The Importer will override any user-supplied values for its
	// Error and Import fields; other fields will be passed
	// through to the type checker.
	TypeChecker types.Config

	// If Loader is non-nil, it is used to satisfy imports.
	//
	// If it is nil, binary object files produced by the gc
	// compiler will be loaded instead of source code for all
	// imported packages.  Such files supply only the types of
	// package-level declarations and values of constants, but no
	// code, so this mode will not yield a whole program.  It is
	// intended for analyses that perform intraprocedural analysis
	// of a single package.
	Loader SourceLoader
}

// SourceLoader is the signature of a function that locates, reads and
// parses a set of source files given an import path.
//
// fset is the fileset to which the ASTs should be added.
// path is the imported path, e.g. "sync/atomic".
//
// On success, the function returns files, the set of ASTs produced,
// or the first error encountered.
//
// The MakeGoBuildLoader utility can be used to construct a
// SourceLoader based on go/build.
//
type SourceLoader func(fset *token.FileSet, path string) (files []*ast.File, err error)

// New returns a new, empty Importer using configuration options
// specified by config.
//
func New(config *Config) *Importer {
	imp := &Importer{
		config:   config,
		Fset:     token.NewFileSet(),
		Packages: make(map[string]*PackageInfo),
		errors:   make(map[string]error),
	}
	imp.config.TypeChecker.Error = func(e error) { fmt.Fprintln(os.Stderr, e) }
	imp.config.TypeChecker.Import = imp.doImport
	return imp
}

// doImport loads the typechecker package identified by path
// Implements the types.Importer prototype.
//
func (imp *Importer) doImport(imports map[string]*types.Package, path string) (pkg *types.Package, err error) {
	// Package unsafe is handled specially, and has no PackageInfo.
	if path == "unsafe" {
		return types.Unsafe, nil
	}

	// Load the source/binary for 'path', type-check it, construct
	// a PackageInfo and update our map (imp.Packages) and the
	// type-checker's map (imports).
	var info *PackageInfo
	if imp.config.Loader != nil {
		info, err = imp.LoadPackage(path)
	} else {
		if info, ok := imp.Packages[path]; ok {
			imports[path] = info.Pkg
			pkg = info.Pkg
			return // positive cache hit
		}

		if err = imp.errors[path]; err != nil {
			return // negative cache hit
		}

		if pkg, err = types.GcImport(imports, path); err == nil {
			info = &PackageInfo{Pkg: pkg}
			imp.Packages[path] = info
		}
	}

	if err == nil {
		// Cache success.
		pkg = info.Pkg
		imports[path] = pkg
		return pkg, nil
	}

	// Cache failure
	imp.errors[path] = err
	return nil, err
}

// LoadPackage loads the package of the specified import-path,
// performs type-checking, and returns the corresponding
// PackageInfo.
//
// Not idempotent!
// Precondition: Importer.config.Loader != nil.
// TODO(adonovan): fix: violated in call from CreatePackageFromArgs!
// Not thread-safe!
// TODO(adonovan): rethink this API.
//
func (imp *Importer) LoadPackage(importPath string) (*PackageInfo, error) {
	if info, ok := imp.Packages[importPath]; ok {
		return info, nil // positive cache hit
	}

	if err := imp.errors[importPath]; err != nil {
		return nil, err // negative cache hit
	}

	if imp.config.Loader == nil {
		panic("Importer.LoadPackage without a SourceLoader")
	}
	files, err := imp.config.Loader(imp.Fset, importPath)
	if err != nil {
		return nil, err
	}
	info := imp.CreateSourcePackage(importPath, files)
	if info.Err != nil {
		return nil, info.Err
	}
	return info, nil
}

// CreateSourcePackage invokes the type-checker on files and returns a
// PackageInfo containing the resulting type-checker package, the
// ASTs, and other type information.
//
// The order of files determines the package initialization order.
//
// importPath is the full name under which this package is known, such
// as appears in an import declaration. e.g. "sync/atomic".
//
// The ParseFiles utility may be helpful for parsing a set of Go
// source files.
//
// The result is always non-nil; the presence of errors is indicated
// by the PackageInfo.Err field.
//
func (imp *Importer) CreateSourcePackage(importPath string, files []*ast.File) *PackageInfo {
	pkgInfo := &PackageInfo{
		Files: files,
		Info: types.Info{
			Types:      make(map[ast.Expr]types.Type),
			Values:     make(map[ast.Expr]exact.Value),
			Objects:    make(map[*ast.Ident]types.Object),
			Implicits:  make(map[ast.Node]types.Object),
			Scopes:     make(map[ast.Node]*types.Scope),
			Selections: make(map[*ast.SelectorExpr]*types.Selection),
		},
	}
	pkgInfo.Pkg, pkgInfo.Err = imp.config.TypeChecker.Check(importPath, imp.Fset, files, &pkgInfo.Info)
	imp.Packages[importPath] = pkgInfo
	return pkgInfo
}
<|endoftext|>


<commit_before>// +build windows

package windows

import (
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/metrics"
	"github.com/mackerelio/mackerel-agent/util/windows"
	"unsafe"
)

// MemoryGenerator XXX
type MemoryGenerator struct {
}

var memoryLogger = logging.GetLogger("metrics.memory")

// NewMemoryGenerator XXX
func NewMemoryGenerator() (*MemoryGenerator, error) {
	return &MemoryGenerator{}, nil
}

// Generate XXX
func (g *MemoryGenerator) Generate() (metrics.Values, error) {
	ret := make(map[string]float64)

	var memoryStatusEx windows.MEMORY_STATUS_EX
	memoryStatusEx.Length = uint32(unsafe.Sizeof(memoryStatusEx))
	r, _, err := windows.GlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memoryStatusEx)))
	if r == 0 {
		return nil, err
	}

	free := float64(memoryStatusEx.AvailPhys) / 1024 * 1000
	total := float64(memoryStatusEx.TotalPhys) / 1024 * 1000
	ret["memory.free"] = free
	ret["memory.total"] = total
	ret["memory.used"] = total - free
	ret["memory.swap_total"] = float64(memoryStatusEx.TotalVirtual) / 1024
	ret["memory.swap_free"] = float64(memoryStatusEx.AvailVirtual) / 1024

	memoryLogger.Debugf("memory : %s", ret)
	return metrics.Values(ret), nil
}
<commit_msg>missed unit conversion.<commit_after>// +build windows

package windows

import (
	"github.com/mackerelio/mackerel-agent/logging"
	"github.com/mackerelio/mackerel-agent/metrics"
	"github.com/mackerelio/mackerel-agent/util/windows"
	"unsafe"
)

// MemoryGenerator XXX
type MemoryGenerator struct {
}

var memoryLogger = logging.GetLogger("metrics.memory")

// NewMemoryGenerator XXX
func NewMemoryGenerator() (*MemoryGenerator, error) {
	return &MemoryGenerator{}, nil
}

// Generate XXX
func (g *MemoryGenerator) Generate() (metrics.Values, error) {
	ret := make(map[string]float64)

	var memoryStatusEx windows.MEMORY_STATUS_EX
	memoryStatusEx.Length = uint32(unsafe.Sizeof(memoryStatusEx))
	r, _, err := windows.GlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memoryStatusEx)))
	if r == 0 {
		return nil, err
	}

	free := float64(memoryStatusEx.AvailPhys) / 1024 * 1024
	total := float64(memoryStatusEx.TotalPhys) / 1024 * 1024
	ret["memory.free"] = free
	ret["memory.total"] = total
	ret["memory.used"] = total - free
	ret["memory.swap_total"] = float64(memoryStatusEx.TotalVirtual) / 1024
	ret["memory.swap_free"] = float64(memoryStatusEx.AvailVirtual) / 1024

	memoryLogger.Debugf("memory : %s", ret)
	return metrics.Values(ret), nil
}
<|endoftext|>


<commit_before>// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/sync"
)

// deviceFolderFileDownloadState holds current download state of a file that
// a remote device has advertised. blockIndexes represends indexes within
// FileInfo.Blocks that the remote device already has, and version represents
// the version of the file that the remote device is downloading.
type deviceFolderFileDownloadState struct {
	blockIndexes []int32
	version      protocol.Vector
	blockSize    int
}

// deviceFolderDownloadState holds current download state of all files that
// a remote device is currently downloading in a specific folder.
type deviceFolderDownloadState struct {
	mut   sync.RWMutex
	files map[string]deviceFolderFileDownloadState
}

// Has returns whether a block at that specific index, and that specific version of the file
// is currently available on the remote device for pulling from a temporary file.
func (p *deviceFolderDownloadState) Has(file string, version protocol.Vector, index int32) bool {
	p.mut.RLock()
	defer p.mut.RUnlock()

	local, ok := p.files[file]

	if !ok || !local.version.Equal(version) {
		return false
	}

	for _, existingIndex := range local.blockIndexes {
		if existingIndex == index {
			return true
		}
	}
	return false
}

// Update updates internal state of what has been downloaded into the temporary
// files by the remote device for this specific folder.
func (p *deviceFolderDownloadState) Update(updates []protocol.FileDownloadProgressUpdate) {
	p.mut.Lock()
	defer p.mut.Unlock()

	for _, update := range updates {
		local, ok := p.files[update.Name]
		if update.UpdateType == protocol.UpdateTypeForget && ok && local.version.Equal(update.Version) {
			delete(p.files, update.Name)
		} else if update.UpdateType == protocol.UpdateTypeAppend {
			if !ok {
				local = deviceFolderFileDownloadState{
					blockIndexes: update.BlockIndexes,
					version:      update.Version,
					blockSize:    int(update.BlockSize),
				}
			} else if !local.version.Equal(update.Version) {
				local.blockIndexes = append(local.blockIndexes[:0], update.BlockIndexes...)
				local.version = update.Version
				local.blockSize = int(update.BlockSize)
			} else {
				local.blockIndexes = append(local.blockIndexes, update.BlockIndexes...)
			}
			p.files[update.Name] = local
		}
	}
}

func (p *deviceFolderDownloadState) BytesDownloaded() int64 {
	var res int64
	for _, state := range p.files {
		// BlockSize is a new field introduced in 1.4.1, thus a fallback
		// is required (will potentially underrepresent downloaded bytes).
		if state.blockSize != 0 {
			res += int64(len(state.blockIndexes) * state.blockSize)
		} else {
			res += int64(len(state.blockIndexes) * protocol.MinBlockSize)
		}
	}
	return res
}

// GetBlockCounts returns a map filename -> number of blocks downloaded.
func (p *deviceFolderDownloadState) GetBlockCounts() map[string]int {
	p.mut.RLock()
	res := make(map[string]int, len(p.files))
	for name, state := range p.files {
		res[name] = len(state.blockIndexes)
	}
	p.mut.RUnlock()
	return res
}

// deviceDownloadState represents the state of all in progress downloads
// for all folders of a specific device.
type deviceDownloadState struct {
	mut     sync.RWMutex
	folders map[string]*deviceFolderDownloadState
}

// Update updates internal state of what has been downloaded into the temporary
// files by the remote device for this specific folder.
func (t *deviceDownloadState) Update(folder string, updates []protocol.FileDownloadProgressUpdate) {
	if t == nil {
		return
	}
	t.mut.RLock()
	f, ok := t.folders[folder]
	t.mut.RUnlock()

	if !ok {
		f = &deviceFolderDownloadState{
			mut:   sync.NewRWMutex(),
			files: make(map[string]deviceFolderFileDownloadState),
		}
		t.mut.Lock()
		t.folders[folder] = f
		t.mut.Unlock()
	}

	f.Update(updates)
}

// Has returns whether block at that specific index, and that specific version of the file
// is currently available on the remote device for pulling from a temporary file.
func (t *deviceDownloadState) Has(folder, file string, version protocol.Vector, index int32) bool {
	if t == nil {
		return false
	}
	t.mut.RLock()
	f, ok := t.folders[folder]
	t.mut.RUnlock()

	if !ok {
		return false
	}

	return f.Has(file, version, index)
}

// GetBlockCounts returns a map filename -> number of blocks downloaded for the
// given folder.
func (t *deviceDownloadState) GetBlockCounts(folder string) map[string]int {
	if t == nil {
		return nil
	}

	t.mut.RLock()
	defer t.mut.RUnlock()

	for name, state := range t.folders {
		if name == folder {
			return state.GetBlockCounts()
		}
	}
	return nil
}

func (t *deviceDownloadState) BytesDownloaded(folder string) int64 {
	if t == nil {
		return 0
	}

	t.mut.RLock()
	defer t.mut.RUnlock()

	for name, state := range t.folders {
		if name == folder {
			return state.BytesDownloaded()
		}
	}
	return 0
}

func newDeviceDownloadState() *deviceDownloadState {
	return &deviceDownloadState{
		mut:     sync.NewRWMutex(),
		folders: make(map[string]*deviceFolderDownloadState),
	}
}
<commit_msg>lib/model: Add missing lock on download-state (fixes #6880) (#6945)<commit_after>// Copyright (C) 2015 The Syncthing Authors.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

package model

import (
	"github.com/syncthing/syncthing/lib/protocol"
	"github.com/syncthing/syncthing/lib/sync"
)

// deviceFolderFileDownloadState holds current download state of a file that
// a remote device has advertised. blockIndexes represends indexes within
// FileInfo.Blocks that the remote device already has, and version represents
// the version of the file that the remote device is downloading.
type deviceFolderFileDownloadState struct {
	blockIndexes []int32
	version      protocol.Vector
	blockSize    int
}

// deviceFolderDownloadState holds current download state of all files that
// a remote device is currently downloading in a specific folder.
type deviceFolderDownloadState struct {
	mut   sync.RWMutex
	files map[string]deviceFolderFileDownloadState
}

// Has returns whether a block at that specific index, and that specific version of the file
// is currently available on the remote device for pulling from a temporary file.
func (p *deviceFolderDownloadState) Has(file string, version protocol.Vector, index int32) bool {
	p.mut.RLock()
	defer p.mut.RUnlock()

	local, ok := p.files[file]

	if !ok || !local.version.Equal(version) {
		return false
	}

	for _, existingIndex := range local.blockIndexes {
		if existingIndex == index {
			return true
		}
	}
	return false
}

// Update updates internal state of what has been downloaded into the temporary
// files by the remote device for this specific folder.
func (p *deviceFolderDownloadState) Update(updates []protocol.FileDownloadProgressUpdate) {
	p.mut.Lock()
	defer p.mut.Unlock()

	for _, update := range updates {
		local, ok := p.files[update.Name]
		if update.UpdateType == protocol.UpdateTypeForget && ok && local.version.Equal(update.Version) {
			delete(p.files, update.Name)
		} else if update.UpdateType == protocol.UpdateTypeAppend {
			if !ok {
				local = deviceFolderFileDownloadState{
					blockIndexes: update.BlockIndexes,
					version:      update.Version,
					blockSize:    int(update.BlockSize),
				}
			} else if !local.version.Equal(update.Version) {
				local.blockIndexes = append(local.blockIndexes[:0], update.BlockIndexes...)
				local.version = update.Version
				local.blockSize = int(update.BlockSize)
			} else {
				local.blockIndexes = append(local.blockIndexes, update.BlockIndexes...)
			}
			p.files[update.Name] = local
		}
	}
}

func (p *deviceFolderDownloadState) BytesDownloaded() int64 {
	p.mut.RLock()
	defer p.mut.RUnlock()

	var res int64
	for _, state := range p.files {
		// BlockSize is a new field introduced in 1.4.1, thus a fallback
		// is required (will potentially underrepresent downloaded bytes).
		if state.blockSize != 0 {
			res += int64(len(state.blockIndexes) * state.blockSize)
		} else {
			res += int64(len(state.blockIndexes) * protocol.MinBlockSize)
		}
	}
	return res
}

// GetBlockCounts returns a map filename -> number of blocks downloaded.
func (p *deviceFolderDownloadState) GetBlockCounts() map[string]int {
	p.mut.RLock()
	res := make(map[string]int, len(p.files))
	for name, state := range p.files {
		res[name] = len(state.blockIndexes)
	}
	p.mut.RUnlock()
	return res
}

// deviceDownloadState represents the state of all in progress downloads
// for all folders of a specific device.
type deviceDownloadState struct {
	mut     sync.RWMutex
	folders map[string]*deviceFolderDownloadState
}

// Update updates internal state of what has been downloaded into the temporary
// files by the remote device for this specific folder.
func (t *deviceDownloadState) Update(folder string, updates []protocol.FileDownloadProgressUpdate) {
	if t == nil {
		return
	}
	t.mut.RLock()
	f, ok := t.folders[folder]
	t.mut.RUnlock()

	if !ok {
		f = &deviceFolderDownloadState{
			mut:   sync.NewRWMutex(),
			files: make(map[string]deviceFolderFileDownloadState),
		}
		t.mut.Lock()
		t.folders[folder] = f
		t.mut.Unlock()
	}

	f.Update(updates)
}

// Has returns whether block at that specific index, and that specific version of the file
// is currently available on the remote device for pulling from a temporary file.
func (t *deviceDownloadState) Has(folder, file string, version protocol.Vector, index int32) bool {
	if t == nil {
		return false
	}
	t.mut.RLock()
	f, ok := t.folders[folder]
	t.mut.RUnlock()

	if !ok {
		return false
	}

	return f.Has(file, version, index)
}

// GetBlockCounts returns a map filename -> number of blocks downloaded for the
// given folder.
func (t *deviceDownloadState) GetBlockCounts(folder string) map[string]int {
	if t == nil {
		return nil
	}

	t.mut.RLock()
	defer t.mut.RUnlock()

	for name, state := range t.folders {
		if name == folder {
			return state.GetBlockCounts()
		}
	}
	return nil
}

func (t *deviceDownloadState) BytesDownloaded(folder string) int64 {
	if t == nil {
		return 0
	}

	t.mut.RLock()
	defer t.mut.RUnlock()

	for name, state := range t.folders {
		if name == folder {
			return state.BytesDownloaded()
		}
	}
	return 0
}

func newDeviceDownloadState() *deviceDownloadState {
	return &deviceDownloadState{
		mut:     sync.NewRWMutex(),
		folders: make(map[string]*deviceFolderDownloadState),
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"http"
	"json"
	"log"
	"os"
	"path"
	"template"
	"time"
	"xml"
)

type Configuration struct {
	UpdateInterval int64
	Feeds          []FeedInfo
}

type FeedInfo struct {
	Name string
	URL  string
	Type string
}

type Feed struct {
	Info  FeedInfo
	Items map[string]Item
}

type Item struct {
	Title   string
	GUID    string
	URL     string
	Date    string // TODO this may need to be a struct
	Desc    string
	Content string
	Read    bool
}

type RSSData struct {
	Channel Channel
}

type Channel struct {
	Title       string
	Link        string
	Description string
	Item        []RSSItemData
}

type RSSItemData struct {
	Title       string
	Link        string
	PubDate     string
	GUID        string
	Description string
	Content     string
}

type TemplateData struct {
	Config Configuration
	Feeds  map[string]Feed
}

var (
	Config        Configuration
	Feeds         map[string]Feed
	TmplData      TemplateData

	page_template template.Template
	page_content  string
	client        http.Client
)

func main() {
	flag.Parse()

	InitTemplate()
	ReadConfig()
	InitCache()
	WriteCache()
	go RunHTTPServer()

	ticks := time.Tick(1e9 * Config.UpdateInterval)
	for {
		ReadFeeds()
		<-ticks
	}
}

func InitTemplate() {
	log.Print("Initializing Page Template")
	page_template.Parse(page_template_string)
}

func ReadConfig() {
	log.Print("Reading Config")
	// Read config from ~/.munchrc
	file, err := os.Open(path.Join(os.Getenv("HOME"),".munchrc"))
	if err != nil {
		log.Fatal(err.String())
	}
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&Config)
	if err != nil {
		log.Fatal(err.String())
	}
}

func InitCache() {
	Feeds = make(map[string]Feed)
	// Ensure the cache directory exists
	cachePath := path.Join(os.Getenv("HOME"), ".munch.d", "cache")
	os.MkdirAll(cachePath, 0700)
	// For each feed
	for _, info := range Config.Feeds {
		name := info.Name
		fPath := path.Join(cachePath, name)
		file, _ := os.Open(fPath)
		if file != nil {
		} else {
			log.Print("New Feed: ", name)
			feed := Feed{}
			feed.Info = info
			feed.Items = make(map[string]Item)
			Feeds[name] = feed
		}
	}
}

func WriteCache() {
	// TODO
}

func ReadFeeds() {
	log.Print("Updating feeds")
	for _, feed := range Feeds {
		switch (feed.Info.Type) {
		case "RSS":
			readRSS(feed)
		default:
			log.Print("Ignoring unknown feed of type ", feed.Info.Type)
		}
	}
	log.Print("Done")
}

func readRSS(feed Feed) {
	url := feed.Info.URL
	log.Print(url)
	r, err := client.Get(url)
	if err != nil {
		log.Print("ERROR: ", err.String())
	}
	reader := r.Body
	feedData := RSSData{}
	err = xml.Unmarshal(reader, &feedData)
	if err != nil {
		log.Print("ERROR: ", err.String())
	}
	// now transform the XML into our internal data structure
	changed := false
	for _, itemData := range feedData.Channel.Item {
		guid := itemData.GUID
		_, ok := feed.Items[guid]
		if !ok {
			// GUID not found - add the item
			changed = true
			item := Item {
				Title: itemData.Title,
				GUID: guid,
				URL: itemData.Link,
				Date: itemData.PubDate,
				Desc: itemData.Description,
				Content: itemData.Content,
				Read: false,
			}
			feed.Items[guid] = item
		}
	}
	if (changed) {
		UpdatePage()
		// TODO run some commands from Config?
	}
}

func UpdatePage() {
	// TODO create a write on page_content
}

func RunHTTPServer() {
	log.Print("Spawning HTTP Server")
	http.HandleFunc("/", HTTPHandler)
	err := http.ListenAndServe("localhost:8090", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err.String())
	}
}

func HTTPHandler(w http.ResponseWriter, req *http.Request) {
	TmplData.Feeds = Feeds
	TmplData.Config = Config
	err := page_template.Execute(w, TmplData)
	if err != nil {
		log.Print("ERROR: ", err.String())
	}
}

<commit_msg>improved error checking<commit_after>package main

import (
	"flag"
	"http"
	"json"
	"log"
	"os"
	"path"
	"template"
	"time"
	"xml"
)

type Configuration struct {
	UpdateInterval int64
	Feeds          []FeedInfo
}

type FeedInfo struct {
	Name string
	URL  string
	Type string
}

type Feed struct {
	Info  FeedInfo
	Items map[string]Item
}

type Item struct {
	Title   string
	GUID    string
	URL     string
	Date    string // TODO this may need to be a struct
	Desc    string
	Content string
	Read    bool
}

type RSSData struct {
	Channel Channel
}

type Channel struct {
	Title       string
	Link        string
	Description string
	Item        []RSSItemData
}

type RSSItemData struct {
	Title       string
	Link        string
	PubDate     string
	GUID        string
	Description string
	Content     string
}

type TemplateData struct {
	Config Configuration
	Feeds  map[string]Feed
}

var (
	Config        Configuration
	Feeds         map[string]Feed
	TmplData      TemplateData

	page_template template.Template
	page_content  string
	client        http.Client
)

func main() {
	flag.Parse()

	InitTemplate()
	ReadConfig()
	InitCache()
	WriteCache()
	go RunHTTPServer()

	ticks := time.Tick(1e9 * Config.UpdateInterval)
	for {
		ReadFeeds()
		<-ticks
	}
}

func InitTemplate() {
	log.Print("Initializing Page Template")
	page_template.Parse(page_template_string)
}

func ReadConfig() {
	log.Print("Reading Config")
	// Read config from ~/.munchrc
	file, err := os.Open(path.Join(os.Getenv("HOME"),".munchrc"))
	if err != nil {
		log.Fatal(err.String())
	}
	decoder := json.NewDecoder(file)
	err = decoder.Decode(&Config)
	if err != nil {
		log.Fatal(err.String())
	}
}

func InitCache() {
	Feeds = make(map[string]Feed)
	// Ensure the cache directory exists
	cachePath := path.Join(os.Getenv("HOME"), ".munch.d", "cache")
	os.MkdirAll(cachePath, 0700)
	// For each feed
	for _, info := range Config.Feeds {
		name := info.Name
		fPath := path.Join(cachePath, name)
		file, _ := os.Open(fPath)
		if file != nil {
		} else {
			log.Print("New Feed: ", name)
			feed := Feed{}
			feed.Info = info
			feed.Items = make(map[string]Item)
			Feeds[name] = feed
		}
	}
}

func WriteCache() {
	// TODO
}

func ReadFeeds() {
	log.Print("Updating feeds")
	for _, feed := range Feeds {
		switch (feed.Info.Type) {
		case "RSS":
			readRSS(feed)
		default:
			log.Print("Ignoring unknown feed of type ", feed.Info.Type)
		}
	}
	log.Print("Done")
}

func readRSS(feed Feed) {
	url := feed.Info.URL
	log.Print(url)
	r, err := client.Get(url)
	if err != nil {
		log.Print("ERROR: ", err.String())
		return
	}
	reader := r.Body
	feedData := RSSData{}
	err = xml.Unmarshal(reader, &feedData)
	if err != nil {
		log.Print("ERROR: ", err.String())
		return
	}
	// now transform the XML into our internal data structure
	changed := false
	for _, itemData := range feedData.Channel.Item {
		guid := itemData.GUID
		_, ok := feed.Items[guid]
		if !ok {
			// GUID not found - add the item
			changed = true
			item := Item {
				Title: itemData.Title,
				GUID: guid,
				URL: itemData.Link,
				Date: itemData.PubDate,
				Desc: itemData.Description,
				Content: itemData.Content,
				Read: false,
			}
			feed.Items[guid] = item
		}
	}
	if (changed) {
		UpdatePage()
		// TODO run some commands from Config?
	}
}

func readAtom(feed Feed) {

}

func readRDF(feed Feed) {

}

func UpdatePage() {
	// TODO create a write on page_content
}

func RunHTTPServer() {
	log.Print("Spawning HTTP Server")
	http.HandleFunc("/", HTTPHandler)
	err := http.ListenAndServe("localhost:8090", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err.String())
	}
}

func HTTPHandler(w http.ResponseWriter, req *http.Request) {
	TmplData.Feeds = Feeds
	TmplData.Config = Config
	err := page_template.Execute(w, TmplData)
	if err != nil {
		log.Print("ERROR: ", err.String())
	}
}

<|endoftext|>


<commit_before>// Copyright 2016 Google 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.

// Package gcslock is a scalable, distributed mutex that can be used
// to serialize computations anywhere on the global internet.
package gcslock

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
)

const (
	defaultStorageLockURL   = "https://www.googleapis.com/upload/storage/v1"
	defaultStorageUnlockURL = "https://www.googleapis.com/storage/v1"
)

var (
	// These vars are used in the requests below. Having separate default
	// values makes it easy to reset the standard config during testing.
	storageLockURL   = defaultStorageLockURL
	storageUnlockURL = defaultStorageUnlockURL
)

// ContextLocker provides an extension of the sync.Locker interface.
type ContextLocker interface {
	sync.Locker
	ContextLock(context.Context) error
	ContextUnlock(context.Context) error
}

type mutex struct {
	bucket string
	object string
	client *http.Client
}

var _ ContextLocker = (*mutex)(nil)

// Lock waits indefinitely to acquire a mutex.
func (m *mutex) Lock() {
	m.ContextLock(context.Background())
}

// ContextLock waits indefinitely to acquire a mutex with timeout
// governed by passed context.
func (m *mutex) ContextLock(ctx context.Context) error {
	q := url.Values{
		"name":              {m.object},
		"uploadType":        {"media"},
		"ifGenerationMatch": {"0"},
	}
	url := fmt.Sprintf("%s/b/%s/o?%s", storageLockURL, m.bucket, q.Encode())
	// NOTE: ctx deadline/timeout and backoff are independent. The former is
	// an aggregate timeout and the latter is a per loop iteration delay.
	backoff := 10 * time.Millisecond
	for {
		req, err := http.NewRequest("POST", url, bytes.NewReader([]byte("1")))
		if err != nil {
			// Likely malformed URL - retry won't fix so return.
			return err
		}
		req.Header.Set("content-type", "text/plain")
		req = req.WithContext(ctx)
		res, err := m.client.Do(req)
		if err == nil {
			res.Body.Close()
			if res.StatusCode == 200 {
				return nil
			}
		}
		select {
		case <-time.After(backoff):
			backoff *= 2
			continue
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// Unlock waits indefinitely to release a mutex.
func (m *mutex) Unlock() {
	m.ContextUnlock(context.Background())
}

// ContextUnlock waits indefinitely to release a mutex with timeout
// governed by passed context.
func (m *mutex) ContextUnlock(ctx context.Context) error {
	url := fmt.Sprintf("%s/b/%s/o/%s?", storageUnlockURL, m.bucket, m.object)
	// NOTE: ctx deadline/timeout and backoff are independent. The former is
	// an aggregate timeout and the latter is a per loop iteration delay.
	backoff := 10 * time.Millisecond
	for {
		req, err := http.NewRequest("DELETE", url, nil)
		if err != nil {
			// Likely malformed URL - retry won't fix so return.
			return err
		}
		req.Header.Set("content-type", "text/plain")
		req = req.WithContext(ctx)
		res, err := m.client.Do(req)
		if err == nil {
			res.Body.Close()
			if res.StatusCode == 204 {
				return nil
			}
		}
		select {
		case <-time.After(backoff):
			backoff *= 2
			continue
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// httpClient is overwritten in tests
var httpClient = func(ctx context.Context) (*http.Client, error) {
	const scope = "https://www.googleapis.com/auth/devstorage.full_control"
	return google.DefaultClient(ctx, scope)
}

// New creates a GCS-based sync.Locker.
// It uses Application Default Credentials to make authenticated requests
// to Google Cloud Storage. See the DefaultClient function of the
// golang.org/x/oauth2/google package for App Default Credentials details.
//
// If ctx argument is nil, context.Background is used.
//
func New(ctx context.Context, bucket, object string) (ContextLocker, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	client, err := httpClient(ctx)
	if err != nil {
		return nil, err
	}
	m := &mutex{
		bucket: bucket,
		object: object,
		client: client,
	}
	return m, nil
}
<commit_msg>remove exptraneous content-type header setting<commit_after>// Copyright 2016 Google 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.

// Package gcslock is a scalable, distributed mutex that can be used
// to serialize computations anywhere on the global internet.
package gcslock

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"sync"
	"time"

	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
)

const (
	defaultStorageLockURL   = "https://www.googleapis.com/upload/storage/v1"
	defaultStorageUnlockURL = "https://www.googleapis.com/storage/v1"
)

var (
	// These vars are used in the requests below. Having separate default
	// values makes it easy to reset the standard config during testing.
	storageLockURL   = defaultStorageLockURL
	storageUnlockURL = defaultStorageUnlockURL
)

// ContextLocker provides an extension of the sync.Locker interface.
type ContextLocker interface {
	sync.Locker
	ContextLock(context.Context) error
	ContextUnlock(context.Context) error
}

type mutex struct {
	bucket string
	object string
	client *http.Client
}

var _ ContextLocker = (*mutex)(nil)

// Lock waits indefinitely to acquire a mutex.
func (m *mutex) Lock() {
	m.ContextLock(context.Background())
}

// ContextLock waits indefinitely to acquire a mutex with timeout
// governed by passed context.
func (m *mutex) ContextLock(ctx context.Context) error {
	q := url.Values{
		"name":              {m.object},
		"uploadType":        {"media"},
		"ifGenerationMatch": {"0"},
	}
	url := fmt.Sprintf("%s/b/%s/o?%s", storageLockURL, m.bucket, q.Encode())
	// NOTE: ctx deadline/timeout and backoff are independent. The former is
	// an aggregate timeout and the latter is a per loop iteration delay.
	backoff := 10 * time.Millisecond
	for {
		req, err := http.NewRequest("POST", url, bytes.NewReader([]byte("1")))
		if err != nil {
			// Likely malformed URL - retry won't fix so return.
			return err
		}
		req.Header.Set("content-type", "text/plain")
		req = req.WithContext(ctx)
		res, err := m.client.Do(req)
		if err == nil {
			res.Body.Close()
			if res.StatusCode == 200 {
				return nil
			}
		}
		select {
		case <-time.After(backoff):
			backoff *= 2
			continue
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// Unlock waits indefinitely to release a mutex.
func (m *mutex) Unlock() {
	m.ContextUnlock(context.Background())
}

// ContextUnlock waits indefinitely to release a mutex with timeout
// governed by passed context.
func (m *mutex) ContextUnlock(ctx context.Context) error {
	url := fmt.Sprintf("%s/b/%s/o/%s?", storageUnlockURL, m.bucket, m.object)
	// NOTE: ctx deadline/timeout and backoff are independent. The former is
	// an aggregate timeout and the latter is a per loop iteration delay.
	backoff := 10 * time.Millisecond
	for {
		req, err := http.NewRequest("DELETE", url, nil)
		if err != nil {
			// Likely malformed URL - retry won't fix so return.
			return err
		}
		req = req.WithContext(ctx)
		res, err := m.client.Do(req)
		if err == nil {
			res.Body.Close()
			if res.StatusCode == 204 {
				return nil
			}
		}
		select {
		case <-time.After(backoff):
			backoff *= 2
			continue
		case <-ctx.Done():
			return ctx.Err()
		}
	}
}

// httpClient is overwritten in tests
var httpClient = func(ctx context.Context) (*http.Client, error) {
	const scope = "https://www.googleapis.com/auth/devstorage.full_control"
	return google.DefaultClient(ctx, scope)
}

// New creates a GCS-based sync.Locker.
// It uses Application Default Credentials to make authenticated requests
// to Google Cloud Storage. See the DefaultClient function of the
// golang.org/x/oauth2/google package for App Default Credentials details.
//
// If ctx argument is nil, context.Background is used.
//
func New(ctx context.Context, bucket, object string) (ContextLocker, error) {
	if ctx == nil {
		ctx = context.Background()
	}
	client, err := httpClient(ctx)
	if err != nil {
		return nil, err
	}
	m := &mutex{
		bucket: bucket,
		object: object,
		client: client,
	}
	return m, nil
}
<|endoftext|>