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

package id3

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"
)

type Id3v24Tag struct {
	Header         Id3v24Header
	ExtendedHeader Id3v24ExtendedHeader
	Frames         map[string][]*Id3v24Frame
}

func id3v24Err(format string, args ...interface{}) error {
	return &ErrFormat{
		Format: "ID3 version 2.4",
		Err:    fmt.Errorf(format, args...),
	}
}

func getSimpleId3v24TextFrame(frames []*Id3v24Frame) string {
	if len(frames) == 0 {
		return ""
	}
	fields, err := GetId3v24TextIdentificationFrame(frames[0])
	if err != nil {
		return ""
	}
	return strings.Join(fields, " ")
}

func (t *Id3v24Tag) Title() string {
	return getSimpleId3v24TextFrame(t.Frames["TIT2"])
}

func (t *Id3v24Tag) Artist() string {
	return getSimpleId3v24TextFrame(t.Frames["TPE1"])
}

func (t *Id3v24Tag) Album() string {
	return getSimpleId3v24TextFrame(t.Frames["TALB"])
}

func (t *Id3v24Tag) Comment() string {
	return ""
}

func (t *Id3v24Tag) Genre() string {
	return getSimpleId3v24TextFrame(t.Frames["TCON"])
}

func (t *Id3v24Tag) Year() time.Time {
	yearStr := getSimpleId3v24TextFrame(t.Frames["TDRC"])
	if len(yearStr) < 4 {
		return time.Time{}
	}

	yearInt, err := strconv.Atoi(yearStr[0:4])
	if err != nil {
		return time.Time{}
	}

	return time.Date(yearInt, time.January, 1, 0, 0, 0, 0, time.UTC)
}

func (t *Id3v24Tag) Track() uint32 {
	track, err := parseLeadingInt(getSimpleId3v24TextFrame(t.Frames["TRCK"]))
	if err != nil {
		return 0
	}
	return uint32(track)
}

func (t *Id3v24Tag) Disc() uint32 {
	disc, err := parseLeadingInt(getSimpleId3v24TextFrame(t.Frames["TPOS"]))
	if err != nil {
		return 0
	}
	return uint32(disc)
}

func (t *Id3v24Tag) CustomFrames() map[string]string {
	info := make(map[string]string)
	for _, frame := range t.Frames["TXXX"] {
		// See "4.2.6. User defined text information frame" at
		// http://id3.org/id3v2.4.0-frames. TXXX frames contain
		// NUL-separated descriptions and values.
		parts, err := GetId3v24TextIdentificationFrame(frame)
		if err == nil && len(parts) == 2 {
			info[parts[0]] = parts[1]
		}
	}
	return info
}

func (t *Id3v24Tag) TagSize() uint32 {
	return 10 + t.Header.Size
}

type Id3v24Header struct {
	MinorVersion byte
	Flags        Id3v24HeaderFlags
	Size         uint32
}

type Id3v24HeaderFlags struct {
	Unsynchronization     bool
	ExtendedHeader        bool
	ExperimentalIndicator bool
	FooterPresent         bool
}

type Id3v24ExtendedHeader struct {
	Size  uint32
	Flags Id3v24ExtendedHeaderFlags
}

type Id3v24ExtendedHeaderFlags struct {
	Update          bool
	CrcDataPresent  bool
	TagRestrictions bool
}

type Id3v24Frame struct {
	Header  Id3v24FrameHeader
	Content []byte
}

type Id3v24FrameHeader struct {
	Id    string
	Size  uint32
	Flags Id3v24FrameHeaderFlags
}

type Id3v24FrameHeaderFlags struct {
	TagAlterPreservation  bool
	FileAlterPreservation bool
	ReadOnly              bool

	GroupingIdentity    bool
	Compression         bool
	Encryption          bool
	Unsynchronization   bool
	DataLengthIndicator bool
}

func Decode24(r io.ReaderAt) (*Id3v24Tag, error) {
	headerBytes := make([]byte, 10)
	if _, err := r.ReadAt(headerBytes, 0); err != nil {
		return nil, err
	}

	header, err := parseId3v24Header(headerBytes)
	if err != nil {
		return nil, err
	}

	br := bufio.NewReader(io.NewSectionReader(r, 10, int64(header.Size)))

	var extendedHeader Id3v24ExtendedHeader
	if header.Flags.ExtendedHeader {
		var err error
		if extendedHeader, err = parseId3v24ExtendedHeader(br); err != nil {
			return nil, err
		}
	}

	result := &Id3v24Tag{
		Header:         header,
		ExtendedHeader: extendedHeader,
		Frames:         make(map[string][]*Id3v24Frame),
	}

	var totalSize uint32
	totalSize += extendedHeader.Size

	for totalSize < header.Size {
		hasFrame, err := hasId3v24Frame(br)
		if err != nil {
			return nil, err
		}

		if !hasFrame {
			break
		}

		frame, err := parseId3v24Frame(br)
		if err != nil {
			return nil, err
		}

		// 10 bytes for the frame header, and the body.
		totalSize += 10 + frame.Header.Size

		result.Frames[frame.Header.Id] = append(result.Frames[frame.Header.Id], frame)
	}
	return result, nil
}

func parseId3v24Header(headerBytes []byte) (result Id3v24Header, err error) {
	if !bytes.Equal(headerBytes[0:4], []byte{'I', 'D', '3', 4}) {
		err = id3v24Err("invalid magic numbers")
		return
	}

	result.MinorVersion = headerBytes[4]

	flags := headerBytes[5]

	result.Flags.Unsynchronization = (flags & (1 << 7)) != 0
	result.Flags.ExtendedHeader = (flags & (1 << 6)) != 0
	result.Flags.ExperimentalIndicator = (flags & (1 << 5)) != 0
	result.Flags.FooterPresent = (flags & (1 << 4)) != 0

	result.Size = uint32(parseBase128Int(headerBytes[6:10]))
	return
}

func parseId3v24ExtendedHeader(br *bufio.Reader) (result Id3v24ExtendedHeader, err error) {
	sizeBytes, err := br.Peek(4)
	if err != nil {
		return
	}

	result.Size = uint32(parseBase128Int(sizeBytes))

	headerBytes := make([]byte, result.Size)
	if _, err = io.ReadFull(br, headerBytes); err != nil {
		return
	}

	// Discard size and number of flags bytes, and store flags.
	_, _, flags, headerBytes := headerBytes[:4], headerBytes[4], headerBytes[5], headerBytes[5:]

	result.Flags.Update = (flags & (1 << 6)) != 0
	result.Flags.CrcDataPresent = (flags & (1 << 5)) != 0
	result.Flags.TagRestrictions = (flags & (1 << 4)) != 0

	// Don't do anything with the rest of the extended header for now.

	return
}

func hasId3v24Frame(br *bufio.Reader) (bool, error) {
	data, err := br.Peek(4)
	if err != nil {
		return false, err
	}

	for _, c := range data {
		if (c < 'A' || 'Z' < c) && (c < '0' || '9' < c) {
			return false, nil
		}
	}
	return true, nil
}

func parseId3v24Frame(br *bufio.Reader) (*Id3v24Frame, error) {
	header, err := parseId3v24FrameHeader(br)
	if err != nil {
		return nil, err
	}

	content := make([]byte, header.Size)
	if _, err := io.ReadFull(br, content); err != nil {
		return nil, err
	}

	return &Id3v24Frame{
		Header:  header,
		Content: content,
	}, nil
}

func parseId3v24FrameHeader(br *bufio.Reader) (result Id3v24FrameHeader, err error) {
	headerBytes := make([]byte, 10)
	if _, err = io.ReadFull(br, headerBytes); err != nil {
		return
	}

	idBytes, sizeBytes, flags := headerBytes[0:4], headerBytes[4:8], headerBytes[8:10]
	result.Id = string(idBytes)

	// Read the size as 4 base128 bytes.
	result.Size = uint32(parseBase128Int(sizeBytes))

	result.Flags.TagAlterPreservation = (flags[0] & (1 << 6)) != 0
	result.Flags.FileAlterPreservation = (flags[0] & (1 << 5)) != 0
	result.Flags.ReadOnly = (flags[0] & (1 << 4)) != 0

	result.Flags.GroupingIdentity = (flags[1] & (1 << 6)) != 0
	result.Flags.Compression = (flags[1] & (1 << 3)) != 0
	result.Flags.Encryption = (flags[1] & (1 << 2)) != 0
	result.Flags.Unsynchronization = (flags[1] & (1 << 1)) != 0
	result.Flags.DataLengthIndicator = (flags[1] & (1 << 0)) != 0

	return result, nil
}
<commit_msg>Permit sub-4-byte padding in ID3 v2.4 tags<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package id3

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strconv"
	"strings"
	"time"
)

type Id3v24Tag struct {
	Header         Id3v24Header
	ExtendedHeader Id3v24ExtendedHeader
	Frames         map[string][]*Id3v24Frame
}

func id3v24Err(format string, args ...interface{}) error {
	return &ErrFormat{
		Format: "ID3 version 2.4",
		Err:    fmt.Errorf(format, args...),
	}
}

func getSimpleId3v24TextFrame(frames []*Id3v24Frame) string {
	if len(frames) == 0 {
		return ""
	}
	fields, err := GetId3v24TextIdentificationFrame(frames[0])
	if err != nil {
		return ""
	}
	return strings.Join(fields, " ")
}

func (t *Id3v24Tag) Title() string {
	return getSimpleId3v24TextFrame(t.Frames["TIT2"])
}

func (t *Id3v24Tag) Artist() string {
	return getSimpleId3v24TextFrame(t.Frames["TPE1"])
}

func (t *Id3v24Tag) Album() string {
	return getSimpleId3v24TextFrame(t.Frames["TALB"])
}

func (t *Id3v24Tag) Comment() string {
	return ""
}

func (t *Id3v24Tag) Genre() string {
	return getSimpleId3v24TextFrame(t.Frames["TCON"])
}

func (t *Id3v24Tag) Year() time.Time {
	yearStr := getSimpleId3v24TextFrame(t.Frames["TDRC"])
	if len(yearStr) < 4 {
		return time.Time{}
	}

	yearInt, err := strconv.Atoi(yearStr[0:4])
	if err != nil {
		return time.Time{}
	}

	return time.Date(yearInt, time.January, 1, 0, 0, 0, 0, time.UTC)
}

func (t *Id3v24Tag) Track() uint32 {
	track, err := parseLeadingInt(getSimpleId3v24TextFrame(t.Frames["TRCK"]))
	if err != nil {
		return 0
	}
	return uint32(track)
}

func (t *Id3v24Tag) Disc() uint32 {
	disc, err := parseLeadingInt(getSimpleId3v24TextFrame(t.Frames["TPOS"]))
	if err != nil {
		return 0
	}
	return uint32(disc)
}

func (t *Id3v24Tag) CustomFrames() map[string]string {
	info := make(map[string]string)
	for _, frame := range t.Frames["TXXX"] {
		// See "4.2.6. User defined text information frame" at
		// http://id3.org/id3v2.4.0-frames. TXXX frames contain
		// NUL-separated descriptions and values.
		parts, err := GetId3v24TextIdentificationFrame(frame)
		if err == nil && len(parts) == 2 {
			info[parts[0]] = parts[1]
		}
	}
	return info
}

func (t *Id3v24Tag) TagSize() uint32 {
	return 10 + t.Header.Size
}

type Id3v24Header struct {
	MinorVersion byte
	Flags        Id3v24HeaderFlags
	Size         uint32
}

type Id3v24HeaderFlags struct {
	Unsynchronization     bool
	ExtendedHeader        bool
	ExperimentalIndicator bool
	FooterPresent         bool
}

type Id3v24ExtendedHeader struct {
	Size  uint32
	Flags Id3v24ExtendedHeaderFlags
}

type Id3v24ExtendedHeaderFlags struct {
	Update          bool
	CrcDataPresent  bool
	TagRestrictions bool
}

type Id3v24Frame struct {
	Header  Id3v24FrameHeader
	Content []byte
}

type Id3v24FrameHeader struct {
	Id    string
	Size  uint32
	Flags Id3v24FrameHeaderFlags
}

type Id3v24FrameHeaderFlags struct {
	TagAlterPreservation  bool
	FileAlterPreservation bool
	ReadOnly              bool

	GroupingIdentity    bool
	Compression         bool
	Encryption          bool
	Unsynchronization   bool
	DataLengthIndicator bool
}

func Decode24(r io.ReaderAt) (*Id3v24Tag, error) {
	headerBytes := make([]byte, 10)
	if _, err := r.ReadAt(headerBytes, 0); err != nil {
		return nil, err
	}

	header, err := parseId3v24Header(headerBytes)
	if err != nil {
		return nil, err
	}

	br := bufio.NewReader(io.NewSectionReader(r, 10, int64(header.Size)))

	var extendedHeader Id3v24ExtendedHeader
	if header.Flags.ExtendedHeader {
		var err error
		if extendedHeader, err = parseId3v24ExtendedHeader(br); err != nil {
			return nil, err
		}
	}

	result := &Id3v24Tag{
		Header:         header,
		ExtendedHeader: extendedHeader,
		Frames:         make(map[string][]*Id3v24Frame),
	}

	var totalSize uint32
	totalSize += extendedHeader.Size

	for totalSize < header.Size {
		hasFrame, err := hasId3v24Frame(br)
		if err != nil {
			return nil, err
		}

		if !hasFrame {
			break
		}

		frame, err := parseId3v24Frame(br)
		if err != nil {
			return nil, err
		}

		// 10 bytes for the frame header, and the body.
		totalSize += 10 + frame.Header.Size

		result.Frames[frame.Header.Id] = append(result.Frames[frame.Header.Id], frame)
	}
	return result, nil
}

func parseId3v24Header(headerBytes []byte) (result Id3v24Header, err error) {
	if !bytes.Equal(headerBytes[0:4], []byte{'I', 'D', '3', 4}) {
		err = id3v24Err("invalid magic numbers")
		return
	}

	result.MinorVersion = headerBytes[4]

	flags := headerBytes[5]

	result.Flags.Unsynchronization = (flags & (1 << 7)) != 0
	result.Flags.ExtendedHeader = (flags & (1 << 6)) != 0
	result.Flags.ExperimentalIndicator = (flags & (1 << 5)) != 0
	result.Flags.FooterPresent = (flags & (1 << 4)) != 0

	result.Size = uint32(parseBase128Int(headerBytes[6:10]))
	return
}

func parseId3v24ExtendedHeader(br *bufio.Reader) (result Id3v24ExtendedHeader, err error) {
	sizeBytes, err := br.Peek(4)
	if err != nil {
		return
	}

	result.Size = uint32(parseBase128Int(sizeBytes))

	headerBytes := make([]byte, result.Size)
	if _, err = io.ReadFull(br, headerBytes); err != nil {
		return
	}

	// Discard size and number of flags bytes, and store flags.
	_, _, flags, headerBytes := headerBytes[:4], headerBytes[4], headerBytes[5], headerBytes[5:]

	result.Flags.Update = (flags & (1 << 6)) != 0
	result.Flags.CrcDataPresent = (flags & (1 << 5)) != 0
	result.Flags.TagRestrictions = (flags & (1 << 4)) != 0

	// Don't do anything with the rest of the extended header for now.

	return
}

func hasId3v24Frame(br *bufio.Reader) (bool, error) {
	data, err := br.Peek(4)
	if err == io.EOF {
		// If there are fewer than 4 bytes remaining, assume that they're
		// padding after the final frame (see section 3.3 of
		// http://id3.org/id3v2.4.0-structure).
		return false, nil
	} else if err != nil {
		return false, err
	}

	for _, c := range data {
		if (c < 'A' || 'Z' < c) && (c < '0' || '9' < c) {
			return false, nil
		}
	}
	return true, nil
}

func parseId3v24Frame(br *bufio.Reader) (*Id3v24Frame, error) {
	header, err := parseId3v24FrameHeader(br)
	if err != nil {
		return nil, err
	}

	content := make([]byte, header.Size)
	if _, err := io.ReadFull(br, content); err != nil {
		return nil, err
	}

	return &Id3v24Frame{
		Header:  header,
		Content: content,
	}, nil
}

func parseId3v24FrameHeader(br *bufio.Reader) (result Id3v24FrameHeader, err error) {
	headerBytes := make([]byte, 10)
	if _, err = io.ReadFull(br, headerBytes); err != nil {
		return
	}

	idBytes, sizeBytes, flags := headerBytes[0:4], headerBytes[4:8], headerBytes[8:10]
	result.Id = string(idBytes)

	// Read the size as 4 base128 bytes.
	result.Size = uint32(parseBase128Int(sizeBytes))

	result.Flags.TagAlterPreservation = (flags[0] & (1 << 6)) != 0
	result.Flags.FileAlterPreservation = (flags[0] & (1 << 5)) != 0
	result.Flags.ReadOnly = (flags[0] & (1 << 4)) != 0

	result.Flags.GroupingIdentity = (flags[1] & (1 << 6)) != 0
	result.Flags.Compression = (flags[1] & (1 << 3)) != 0
	result.Flags.Encryption = (flags[1] & (1 << 2)) != 0
	result.Flags.Unsynchronization = (flags[1] & (1 << 1)) != 0
	result.Flags.DataLengthIndicator = (flags[1] & (1 << 0)) != 0

	return result, nil
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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
//
// 		https://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 main

import (
	"fmt"
	"strings"
)

type commit struct {
	category string
	message  string
}

func newCommit(line string) *commit {
	var cat, msg string
	lineSplit := strings.SplitN(line, ":", 2)
	if len(lineSplit) > 1 {
		cat = strings.TrimSpace(lineSplit[0])
		msg = strings.TrimSpace(lineSplit[1])
	} else {
		cat = "other"
		msg = strings.TrimSpace(lineSplit[0])
	}
	return &commit{category: cat, message: msg}
}

func (c *commit) String() string {
	return c.message
}

type changelog struct {
	gapic  []*commit
	bazel  []*commit
	gencli []*commit
	chore  []*commit
	other  []*commit
}

func newChangelog(gitlog string) *changelog {
	var gapic, bazel, gencli, chore, other []*commit
	var hasDeps bool
	for _, line := range strings.Split(gitlog, "\n") {
		cmt := newCommit(line)
		switch cmt.category {
		case "gapic":
			gapic = append(gapic, cmt)
		case "bazel":
			bazel = append(bazel, cmt)
		case "gencli":
			gencli = append(gencli, cmt)
		case "chore(deps)":
			hasDeps = true
		case "chore":
			chore = append(chore, cmt)
		default:
			other = append(other, cmt)
		}
	}

	if hasDeps {
		chore = append(chore, &commit{category: "chore", message: "update dependencies (see history)"})
	}

	return &changelog{
		gapic:  gapic,
		bazel:  bazel,
		gencli: gencli,
		chore:  chore,
		other:  other,
	}
}

func (cl *changelog) notes() string {
	section := func(title string, commits []*commit) string {
		if len(commits) > 0 {
			// %0A is newline: https://github.community/t/set-output-truncates-multiline-strings/16852
			answer := fmt.Sprintf("## %s%%0A%%0A", title)
			for _, cmt := range commits {
				answer += fmt.Sprintf("* %s%%0A", cmt)
			}
			return answer + "%0A"
		}
		return ""
	}
	return strings.TrimSuffix(
		section("gapic", cl.gapic)+section("bazel", cl.bazel)+section("gencli", cl.gencli)+section("chore", cl.chore)+section("other", cl.other),
		"%0A",
	)
}
<commit_msg>chore: group fix(deps) with chore(deps) in notes (#578)<commit_after>// Copyright 2021 Google LLC
//
// 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
//
// 		https://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 main

import (
	"fmt"
	"strings"
)

type commit struct {
	category string
	message  string
}

func newCommit(line string) *commit {
	var cat, msg string
	lineSplit := strings.SplitN(line, ":", 2)
	if len(lineSplit) > 1 {
		cat = strings.TrimSpace(lineSplit[0])
		msg = strings.TrimSpace(lineSplit[1])
	} else {
		cat = "other"
		msg = strings.TrimSpace(lineSplit[0])
	}
	return &commit{category: cat, message: msg}
}

func (c *commit) String() string {
	return c.message
}

type changelog struct {
	gapic  []*commit
	bazel  []*commit
	gencli []*commit
	chore  []*commit
	other  []*commit
}

func newChangelog(gitlog string) *changelog {
	var gapic, bazel, gencli, chore, other []*commit
	var hasDeps bool
	for _, line := range strings.Split(gitlog, "\n") {
		cmt := newCommit(line)
		switch cmt.category {
		case "gapic":
			gapic = append(gapic, cmt)
		case "bazel":
			bazel = append(bazel, cmt)
		case "gencli":
			gencli = append(gencli, cmt)
		case "fix(deps)":
			fallthrough
		case "chore(deps)":
			hasDeps = true
		case "chore":
			chore = append(chore, cmt)
		default:
			other = append(other, cmt)
		}
	}

	if hasDeps {
		chore = append(chore, &commit{category: "chore", message: "update dependencies (see history)"})
	}

	return &changelog{
		gapic:  gapic,
		bazel:  bazel,
		gencli: gencli,
		chore:  chore,
		other:  other,
	}
}

func (cl *changelog) notes() string {
	section := func(title string, commits []*commit) string {
		if len(commits) > 0 {
			// %0A is newline: https://github.community/t/set-output-truncates-multiline-strings/16852
			answer := fmt.Sprintf("## %s%%0A%%0A", title)
			for _, cmt := range commits {
				answer += fmt.Sprintf("* %s%%0A", cmt)
			}
			return answer + "%0A"
		}
		return ""
	}
	return strings.TrimSuffix(
		section("gapic", cl.gapic)+section("bazel", cl.bazel)+section("gencli", cl.gencli)+section("chore", cl.chore)+section("other", cl.other),
		"%0A",
	)
}
<|endoftext|>


<commit_before>package lib

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestStatusCountOnNew(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/foo", "a"))

	count, err := statusCount(repo.Repo)
	check(t, err)

	assert.Equal(t, 1, count)
}

func TestStatusCountOnEdit(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/foo", "a"))
	check(t, repo.Commit("first"))

	check(t, repo.WriteContent("app-a/foo", "b"))

	count, err := statusCount(repo.Repo)
	check(t, err)

	assert.Equal(t, 1, count)
}

func TestStatusCountOnRename(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/foo", "a"))
	check(t, repo.Commit("first"))

	check(t, repo.Rename("app-a/foo", "app-a/bar"))

	count, err := statusCount(repo.Repo)
	check(t, err)

	assert.Equal(t, 2, count)
}

func TestInvalidBranch(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.Commit("first"))

	c, err := getBranchCommit(repo.Repo, "foo")

	assert.Nil(t, c)
	assert.EqualError(t, err, "mbt: no reference found for shorthand 'foo'")
}

func TestBranchName(t *testing.T) {
	clean()

	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.Commit("first"))
	check(t, repo.SwitchToBranch("feature"))

	branch, err := getBranchName(repo.Repo)
	check(t, err)

	assert.Equal(t, "feature", branch)
}

func TestDiffByIndex(t *testing.T) {
	clean()

	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/test.txt", "test contents"))
	check(t, repo.Commit("first"))

	check(t, repo.WriteContent("app-a/test.txt", "ammend contents"))

	diff, err := getDiffFromIndex(repo.Repo)
	check(t, err)

	n, err := diff.NumDeltas()
	check(t, err)

	assert.Equal(t, 1, n)
}
<commit_msg>Fix: typo<commit_after>package lib

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestStatusCountOnNew(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/foo", "a"))

	count, err := statusCount(repo.Repo)
	check(t, err)

	assert.Equal(t, 1, count)
}

func TestStatusCountOnEdit(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/foo", "a"))
	check(t, repo.Commit("first"))

	check(t, repo.WriteContent("app-a/foo", "b"))

	count, err := statusCount(repo.Repo)
	check(t, err)

	assert.Equal(t, 1, count)
}

func TestStatusCountOnRename(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/foo", "a"))
	check(t, repo.Commit("first"))

	check(t, repo.Rename("app-a/foo", "app-a/bar"))

	count, err := statusCount(repo.Repo)
	check(t, err)

	assert.Equal(t, 2, count)
}

func TestInvalidBranch(t *testing.T) {
	clean()
	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.Commit("first"))

	c, err := getBranchCommit(repo.Repo, "foo")

	assert.Nil(t, c)
	assert.EqualError(t, err, "mbt: no reference found for shorthand 'foo'")
}

func TestBranchName(t *testing.T) {
	clean()

	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.Commit("first"))
	check(t, repo.SwitchToBranch("feature"))

	branch, err := getBranchName(repo.Repo)
	check(t, err)

	assert.Equal(t, "feature", branch)
}

func TestDiffByIndex(t *testing.T) {
	clean()

	repo, err := createTestRepository(".tmp/repo")
	check(t, err)

	check(t, repo.InitModule("app-a"))
	check(t, repo.WriteContent("app-a/test.txt", "test contents"))
	check(t, repo.Commit("first"))

	check(t, repo.WriteContent("app-a/test.txt", "amend contents"))

	diff, err := getDiffFromIndex(repo.Repo)
	check(t, err)

	n, err := diff.NumDeltas()
	check(t, err)

	assert.Equal(t, 1, n)
}
<|endoftext|>


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

package memory

import (
	"fmt"
	"strconv"
	"strings"
)

type MemSize struct {
	sizeInBytes int64
}

const (
	bYTE = 1
	kILO = 1024 * bYTE
	mEGA = 1024 * kILO
	gIGA = 1024 * mEGA
)

var MS_ZERO *MemSize

func init() {
	MS_ZERO, _ = NewMemSize("0")
}

func NewMemSize(ms string) (*MemSize, error) {
	ms = strings.TrimSpace(ms)
	var bytes int64 = 0
	if ms != "0" {
		factor, intStr := int64(1), ms[:len(ms)-1]
		switch ms[len(ms)-1] {
		case 'b', 'B':
			factor = bYTE
		case 'k', 'K':
			factor = kILO
		case 'm', 'M':
			factor = mEGA
		case 'g', 'G':
			factor = gIGA
		default:
			return nil, fmt.Errorf("invalid memory size string '%s'", ms)
		}

		num, err := strconv.ParseInt(intStr, 10, 64)
		if err != nil {
			return nil, err
		}

		bytes = num * factor
	}
	return &MemSize{bytes}, nil
}

func (ms *MemSize) Bytes() int64 {
	return ms.sizeInBytes
}

func (ms *MemSize) Kilos() int64 {
	return ms.sizeInBytes / kILO
}

func (ms *MemSize) Megas() int64 {
	return ms.sizeInBytes / mEGA
}

func (ms *MemSize) Gigas() int64 {
	return ms.sizeInBytes / gIGA
}

func (ms *MemSize) String() string {
	var (
		val  int64
		suff string
	)
	if v := ms.Gigas(); v > 0 {
		val, suff = v, "G"
	} else if v := ms.Megas(); v > 0 {
		val, suff = v, "M"
	} else if v := ms.Kilos(); v > 0 {
		val, suff = v, "K"
	} else {
		return "0"
	}
	return fmt.Sprintf("%d%s", val, suff)
}

func (ms *MemSize) LessThan(other *MemSize) bool {
	return ms.Bytes() < other.Bytes()
}

func (ms *MemSize) Add(other *MemSize) *MemSize {
	return &MemSize{ms.sizeInBytes + other.sizeInBytes}
}

func (ms *MemSize) Scale(factor float64) *MemSize {
	return &MemSize{int64(factor*float64(ms.sizeInBytes) + 0.5)}
}

func (ms *MemSize) Equals(other *MemSize) bool {
	return ms.sizeInBytes == other.sizeInBytes
}

func (ms *MemSize) Empty() bool {
	return ms.sizeInBytes == 0
}

func (ms *MemSize) DividedBy(other *MemSize) float64 {
	return float64(ms.sizeInBytes) / float64(other.sizeInBytes)
}
<commit_msg>Some doc for MemSize exported functions in memory_size.go.<commit_after>// Encoding: utf-8
// Cloud Foundry Java Buildpack
// Copyright (c) 2015 the original author or authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package memory

import (
	"fmt"
	"strconv"
	"strings"
)

// memory_size.go defines the MemSize struct which captures a memory size
// allocation. It understands the normal nK, nM, nG string representations,
// and permits scaling and comparison operations.  The methods are described
// in-line.
// type MemSize is exported, all the methods take *MemSize

type MemSize struct {
	sizeInBytes int64
}

const (
	bYTE = 1
	kILO = 1024 * bYTE
	mEGA = 1024 * kILO
	gIGA = 1024 * mEGA
)

// The empty memory allocation (not nil).
var MS_ZERO *MemSize

func init() {
	MS_ZERO, _ = NewMemSize("0")
}

// Construct a new MemSize object from a string description
//
// Errors include:
//	errors from ParseInt
//	error invalid memory size string '%s'
func NewMemSize(ms string) (*MemSize, error) {
	ms = strings.TrimSpace(ms)
	var bytes int64 = 0
	if ms != "0" {
		factor, intStr := int64(1), ms[:len(ms)-1]
		switch ms[len(ms)-1] {
		case 'b', 'B':
			factor = bYTE
		case 'k', 'K':
			factor = kILO
		case 'm', 'M':
			factor = mEGA
		case 'g', 'G':
			factor = gIGA
		default:
			return nil, fmt.Errorf("invalid memory size string '%s'", ms)
		}

		num, err := strconv.ParseInt(intStr, 10, 64)
		if err != nil {
			return nil, err
		}

		bytes = num * factor
	}
	return &MemSize{bytes}, nil
}

// The number of bytes in the MemSize
func (ms *MemSize) Bytes() int64 {
	return ms.sizeInBytes
}

// The number of (whole) kilobytes in the MemSize
func (ms *MemSize) Kilos() int64 {
	return ms.sizeInBytes / kILO
}

// The number of (whole) megabytes in the MemSize
func (ms *MemSize) Megas() int64 {
	return ms.sizeInBytes / mEGA
}

// The number of (whole) gigabytes in the MemSize
func (ms *MemSize) Gigas() int64 {
	return ms.sizeInBytes / gIGA
}

// A string presentation of the MemSize rounded down to whole numbers
// of giga-, mega-, kilo- bytes, and using the K,M,G suffices.
// Less than 1K produces "0" as the string output.
func (ms *MemSize) String() string {
	var (
		val  int64
		suff string
	)
	if v := ms.Gigas(); v > 0 {
		val, suff = v, "G"
	} else if v := ms.Megas(); v > 0 {
		val, suff = v, "M"
	} else if v := ms.Kilos(); v > 0 {
		val, suff = v, "K"
	} else {
		return "0"
	}
	return fmt.Sprintf("%d%s", val, suff)
}

// True if the receiver has less bytes in it than does other.
func (ms *MemSize) LessThan(other *MemSize) bool {
	return ms.Bytes() < other.Bytes()
}

// Produce a new MemSize with the sum of the number of bytes in receiver and other.
func (ms *MemSize) Add(other *MemSize) *MemSize {
	return &MemSize{ms.sizeInBytes + other.sizeInBytes}
}

// Produce a new MemSize with factor times the number of bytes in it (rounded to nearest integer).
func (ms *MemSize) Scale(factor float64) *MemSize {
	return &MemSize{int64(factor*float64(ms.sizeInBytes) + 0.5)}
}

// True if the receiver has exactly the same number of bytes in it as does other.
func (ms *MemSize) Equals(other *MemSize) bool {
	return ms.sizeInBytes == other.sizeInBytes
}

// True if the receiver has exactly zero bytes in it.
func (ms *MemSize) Empty() bool {
	return ms.sizeInBytes == 0
}

// The ratio of the sizes in receiver and other as a floating point number.
// twoGig.DividedBy(oneGig) should return 2.0.
// oneGig.DividedBy(twoGig) should return 0.5.
func (ms *MemSize) DividedBy(other *MemSize) float64 {
	return float64(ms.sizeInBytes) / float64(other.sizeInBytes)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	sm "github.com/ifn/go-statemachine"
)

type DeskMsg struct {
	Desk []string `json:"desk"`
}

type PlayerMsg struct {
	Cmd  sm.EventType `json:"command"`
	Card string       `json:"card"`
}

//

var Suits []string = []string{
	"S",
	"C",
	"H",
	"D",
}

var CardValues map[string]int = map[string]int{
	"6": 6, "7": 7, "8": 8, "9": 9, "10": 10,
	"J": 11,
	"Q": 12,
	"K": 13,
	"A": 14,
}

func higher(c0, c1, t string) int {
	// c0 and c1 have the same suit
	if c0[0] == c1[0] {
		if CardValues[c0[1:]] > CardValues[c1[1:]] {
			return 1
		}
		if CardValues[c0[1:]] < CardValues[c1[1:]] {
			return -1
		}
		return 0
	}
	// c0 is trump, c1 is not
	if c0[:1] == t {
		return 1
	}
	// c1 is trump, c0 is not
	if c1[:1] == t {
		return -1
	}
	// suits are different, both are not trump
	return -2
}

var CardRE *regexp.Regexp = regexp.MustCompile(`[SCHD]([6-9JQKA]|10)`)

func isValid(c string) bool {
	return CardRE.MatchString(c)
}

//

const (
	cmdStart sm.EventType = iota
	cmdMove

	cmdCount
)

const (
	stateCollection sm.State = iota

	stateAttack
	stateDefense

	stateCount
)

type roundResult int

const (
	None roundResult = iota
	Beat
	NotBeat
)

func stateToString(s sm.State) string {
	return [...]string{
		stateCollection: "COLLECTION",
		stateDefense:    "DEFENSE",
		stateAttack:     "ATTACK",
	}[s]
}

func cmdToString(t sm.EventType) string {
	return [...]string{
		cmdStart: "START",
		cmdMove:  "MOVE",
	}[t]
}

//

type cmdArgs struct {
	conn *playerConn
	card string
}

//

func logOutOfTurn(pconn *playerConn) {
	log.Printf("out of turn: %v", pconn.conn.RemoteAddr())
}

func logWontBeat(c1, c2, t string) {
	log.Printf("%v won't bit %v, trump is %v", c1, c2, t)
}

func logWrongNumber(n int) {
	log.Printf("wrong number of players: %v", n)
}

func logStrangeCard(c string, pc *playerConn) {
	log.Printf("%v doesn't own %v", pc, c)
}

//

type gameState struct {
	// 1. fields that don't change during a game

	sm  *sm.StateMachine
	hub *hub

	// trump suit
	trump string

	// 2. fields that don't change during a round

	deck []string

	// attacker that started a round
	aconnStart *playerConn
	// defender
	dconn *playerConn

	// 3. fields that change during a round

	desk []string

	// attacker
	aconn *playerConn
	// card that should be beaten
	cardToBeat string
}

func (self *gameState) popCard() (card string) {
	if deck := self.deck; len(deck) > 0 {
		card = deck[0]
		self.deck = deck[1:]
	}
	return
}

func (self *gameState) initDeck() {
	numCards := len(Suits) * len(CardValues)

	deck := make([]string, 0, numCards)
	for _, suit := range Suits {
		for cv := range CardValues {
			deck = append(deck, suit+cv)
		}
	}

	order := rand.Perm(numCards)
	for _, pos := range order {
		self.deck = append(self.deck, deck[pos])
	}
}

//TODO: don't like it
func (self *gameState) setTrump(card string) {
	tcard := self.popCard()
	if tcard == "" {
		tcard = card
	} else {
		self.deck = append(self.deck, tcard)
	}
	self.trump = tcard[:1]
}

func (self *gameState) nextPlayer(c *playerConn) *playerConn {
	return self.hub.conns.(*mapRing).Next(c).(*playerConn)
}

func (self *gameState) chooseStarting() *playerConn {
	conns := self.hub.conns.(*mapRing)

	return conns.Nth(rand.Intn(conns.Len())).(*playerConn)
}

func (self *gameState) markInactive() {
	if len(self.deck) == 0 {
		for pc := range self.hub.conns.Enumerate() {
			if pc := pc.(*playerConn); len(pc.cards) == 0 {
				pc.active = false
			}
		}
	}
}

func (self *gameState) firstActive(c *playerConn) *playerConn {
	for pc := range self.hub.conns.(*mapRing).EnumerateFrom(c) {
		if pc := pc.(*playerConn); pc.active {
			return pc
		}
	}
	return nil
}

func (self *gameState) setRoles(res roundResult) {
	switch res {
	case None:
		self.aconn = self.chooseStarting()
	case Beat:
		self.aconn = self.dconn
	case NotBeat:
		self.aconn = self.nextPlayer(self.dconn)
	}
	self.dconn = self.nextPlayer(self.aconn)
	self.aconnStart = self.aconn
}

func (self *gameState) dealCards() (card string) {
	for i := 0; i < 6; i++ {
		for pc := range self.hub.conns.Enumerate() {
			card = pc.(*playerConn).fromDeck()
		}
	}
	return
}

func (self *gameState) takeCards() {
	conns := self.hub.conns.(*mapRing)

	takeCards := func(pc *playerConn) {
		for len(self.deck) > 0 && len(pc.cards) < 6 {
			pc.fromDeck()
		}
	}

	for pc := range conns.EnumerateFrom(self.aconnStart) {
		if pc := pc.(*playerConn); pc != self.dconn {
			takeCards(pc)
		}
	}
	takeCards(self.dconn)
}

func (self *gameState) newRound(res roundResult) {
	switch res {
	case None:
		self.initDeck()
		card := self.dealCards()
		self.setTrump(card)
	case NotBeat:
		self.dconn.fromDesk()
		self.takeCards()
	case Beat:
		self.desk = self.desk[:0]
		self.takeCards()
	}
	self.markInactive()
	self.setRoles(res)
	self.cardToBeat = ""
}

// event handlers
// event handlers are actually transition functions.
// in case error event handler should neither change the gameState,
// nor return the state value different from passed to it as an argument.

func (self *gameState) handleStartInCollection(s sm.State, e *sm.Event) sm.State {
	if n := self.hub.conns.(*mapRing).Len(); n < 2 || n > 6 {
		logWrongNumber(n)
		return s
	}

	self.newRound(None)
	return stateAttack
}

func (self *gameState) handleMoveInAttack(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.aconn {
		logOutOfTurn(conn)
		return s
	}

	// attacker sent the card
	if card != "" {
		// throw card to desk
		if _, ok := conn.cards[card]; !ok {
			logStrangeCard(card, conn)
			return s
		}
		conn.toDesk(card)

		self.cardToBeat = card
		return stateDefense
	}

	// attacker sent no card

	aconn := self.nextPlayer(self.aconn)
	if aconn == self.dconn {
		aconn = self.nextPlayer(aconn)
	}

	// check if all attackers have been polled
	if aconn == self.aconnStart {
		self.newRound(Beat)
		return stateAttack
	}

	self.aconn = aconn
	return stateAttack
}

func (self *gameState) handleMoveInDefense(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.dconn {
		logOutOfTurn(conn)
		return s
	}

	// defender takes the cards
	if card == "" {
		self.newRound(NotBeat)
		return stateAttack
	}

	// check that the sent card is capable to beat
	if higher(card, self.cardToBeat, self.trump) != 1 {
		logWontBeat(card, self.cardToBeat, self.trump)
		return s
	}

	// throw card to desk
	if _, ok := conn.cards[card]; !ok {
		logStrangeCard(card, conn)
		return s
	}
	conn.toDesk(card)

	return stateAttack
}

func (self *gameState) showDesk(s sm.State, e *sm.Event) sm.State {
	desk, err := json.Marshal(DeskMsg{self.desk})
	if err != nil {
		log.Println(err)
		return s
	}

	self.hub.bcastChan <- desk

	return s
}

func (self *gameState) log(s sm.State, e *sm.Event) sm.State {
	log.Println(self)
	return s
}

//

func NewGameState() *gameState {
	gst := new(gameState)

	gst.sm = sm.New(stateCollection, uint(stateCount), uint(cmdCount))

	gst.sm.OnChain(cmdStart,
		[]sm.State{stateCollection},
		[]sm.EventHandler{
			gst.handleStartInCollection,
			gst.showDesk,
			gst.log,
		},
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack},
		gst.handleMoveInAttack,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateDefense},
		gst.handleMoveInDefense,
	)

	gst.sm.OnChain(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		[]sm.EventHandler{
			gst.showDesk,
			gst.log,
		},
	)

	gst.deck = make([]string, 0, len(Suits)*len(CardValues)+ /*for trump*/ 1)
	gst.desk = make([]string, 0, 12)

	gst.hub = NewHub()

	return gst
}

//

type playerConn struct {
	gst *gameState

	cards  map[string]struct{}
	active bool

	conn      *websocket.Conn
	hubToConn chan []byte
}

func (self *playerConn) fromDeck() (card string) {
	if card = self.gst.popCard(); card != "" {
		self.cards[card] = struct{}{}
	}
	return
}

func (self *playerConn) toDesk(card string) {
	delete(self.cards, card)

	self.gst.desk = append(self.gst.desk, card)
}

func (self *playerConn) fromDesk() {
	for _, card := range self.gst.desk {
		self.cards[card] = struct{}{}
	}

	self.gst.desk = self.gst.desk[:0]
}

func (self *playerConn) write() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		select {
		case m, ok := <-self.hubToConn:
			if !ok {
				return
			}
			//TODO: text or binary?
			err := self.conn.WriteMessage(websocket.TextMessage, m)
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (self *playerConn) read() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		var m PlayerMsg

		err := self.conn.ReadJSON(&m)
		if err != nil {
			log.Println(err)
			return
		}

		var event *sm.Event
		switch m.Cmd {
		case cmdStart:
			event = &sm.Event{cmdStart, nil}
		case cmdMove:
			event = &sm.Event{cmdMove, cmdArgs{self, m.Card}}
		default:
			log.Printf("unknown command: %v", m.Cmd)
			continue
		}

		err = self.gst.sm.Emit(event)
		if err != nil {
			log.Println(err)
		}
	}
}

//

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

func playerHandler(gst *gameState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}

		p := &playerConn{gst, make(map[string]struct{}), true, conn, make(chan []byte)}

		gst.hub.regChan <- p
		defer func() {
			gst.hub.unregChan <- p
		}()

		go p.write()
		p.read()
	}
}

//

func startDurakSrv() error {
	gst := NewGameState()

	http.HandleFunc("/", playerHandler(gst))

	return http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func main() {
	err := startDurakSrv()
	if err != nil {
		log.Fatal(err)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())

	runtime.GOMAXPROCS(runtime.NumCPU())
}
<commit_msg>minor changes<commit_after>package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	sm "github.com/ifn/go-statemachine"
)

type DeskMsg struct {
	Desk []string `json:"desk"`
}

type PlayerMsg struct {
	Cmd  sm.EventType `json:"command"`
	Card string       `json:"card"`
}

//

var Suits []string = []string{
	"S",
	"C",
	"H",
	"D",
}

var CardValues map[string]int = map[string]int{
	"6": 6, "7": 7, "8": 8, "9": 9, "10": 10,
	"J": 11,
	"Q": 12,
	"K": 13,
	"A": 14,
}

func higher(c0, c1, t string) int {
	// c0 and c1 have the same suit
	if c0[0] == c1[0] {
		if CardValues[c0[1:]] > CardValues[c1[1:]] {
			return 1
		}
		if CardValues[c0[1:]] < CardValues[c1[1:]] {
			return -1
		}
		return 0
	}
	// c0 is trump, c1 is not
	if c0[:1] == t {
		return 1
	}
	// c1 is trump, c0 is not
	if c1[:1] == t {
		return -1
	}
	// suits are different, both are not trump
	return -2
}

var CardRE *regexp.Regexp = regexp.MustCompile(`[SCHD]([6-9JQKA]|10)`)

func isValid(c string) bool {
	return CardRE.MatchString(c)
}

//

const (
	cmdStart sm.EventType = iota
	cmdMove

	cmdCount
)

const (
	stateCollection sm.State = iota

	stateAttack
	stateDefense

	stateCount
)

type roundResult int

const (
	None roundResult = iota
	Beat
	NotBeat
)

func stateToString(s sm.State) string {
	return [...]string{
		stateCollection: "COLLECTION",
		stateDefense:    "DEFENSE",
		stateAttack:     "ATTACK",
	}[s]
}

func cmdToString(t sm.EventType) string {
	return [...]string{
		cmdStart: "START",
		cmdMove:  "MOVE",
	}[t]
}

//

type cmdArgs struct {
	conn *playerConn
	card string
}

//

func logOutOfTurn(pconn *playerConn) {
	log.Printf("out of turn: %v", pconn.conn.RemoteAddr())
}

func logWontBeat(c1, c2, t string) {
	log.Printf("%v won't bit %v, trump is %v", c1, c2, t)
}

func logWrongNumber(n int) {
	log.Printf("wrong number of players: %v", n)
}

func logStrangeCard(c string, pc *playerConn) {
	log.Printf("%v doesn't own %v", pc, c)
}

//

type gameState struct {
	// 1. fields that don't change during a game

	sm  *sm.StateMachine
	hub *hub

	// trump suit
	trump string

	// 2. fields that don't change during a round

	deck []string

	// attacker that started a round
	aconnStart *playerConn
	// defender
	dconn *playerConn

	// 3. fields that change during a round

	desk []string

	// attacker
	aconn *playerConn
	// card that should be beaten
	cardToBeat string
}

func (self *gameState) markInactive() {
	if len(self.deck) == 0 {
		for pc := range self.hub.conns.Enumerate() {
			if pc := pc.(*playerConn); len(pc.cards) == 0 {
				pc.active = false
			}
		}
	}
}

func (self *gameState) firstActive(c *playerConn) *playerConn {
	for pc := range self.hub.conns.(*mapRing).EnumerateFrom(c) {
		if pc := pc.(*playerConn); pc.active {
			return pc
		}
	}
	return nil
}

//

func (self *gameState) nextPlayer(c *playerConn) *playerConn {
	return self.hub.conns.(*mapRing).Next(c).(*playerConn)
}

func (self *gameState) nextActivePlayer(c *playerConn) *playerConn {
	return self.firstActive(self.nextPlayer(c))
}

//

func (self *gameState) initDeck() {
	numCards := len(Suits) * len(CardValues)

	deck := make([]string, 0, numCards)
	for _, suit := range Suits {
		for cv := range CardValues {
			deck = append(deck, suit+cv)
		}
	}

	order := rand.Perm(numCards)
	for _, pos := range order {
		self.deck = append(self.deck, deck[pos])
	}
}

func (self *gameState) popCard() (card string) {
	if deck := self.deck; len(deck) > 0 {
		card = deck[0]
		self.deck = deck[1:]
	}
	return
}

func (self *gameState) dealCards() (card string) {
	for i := 0; i < 6; i++ {
		for pc := range self.hub.conns.Enumerate() {
			card = pc.(*playerConn).fromDeck()
		}
	}
	return
}

func (self *gameState) takeCards() {
	conns := self.hub.conns.(*mapRing)

	takeCards := func(pc *playerConn) {
		for len(self.deck) > 0 && len(pc.cards) < 6 {
			pc.fromDeck()
		}
	}

	for pc := range conns.EnumerateFrom(self.aconnStart) {
		if pc := pc.(*playerConn); pc != self.dconn {
			takeCards(pc)
		}
	}
	takeCards(self.dconn)
}

//

//TODO: don't like it
func (self *gameState) setTrump(card string) {
	tcard := self.popCard()
	if tcard == "" {
		tcard = card
	} else {
		self.deck = append(self.deck, tcard)
	}
	self.trump = tcard[:1]
}

func (self *gameState) chooseStarting() *playerConn {
	conns := self.hub.conns.(*mapRing)

	return conns.Nth(rand.Intn(conns.Len())).(*playerConn)
}

//

func (self *gameState) setRoles(res roundResult) {
	switch res {
	case None:
		self.aconn = self.chooseStarting()
	case Beat:
		self.aconn = self.dconn
	case NotBeat:
		self.aconn = self.nextPlayer(self.dconn)
	}
	self.dconn = self.nextPlayer(self.aconn)
	self.aconnStart = self.aconn
}

func (self *gameState) newRound(res roundResult) {
	switch res {
	case None:
		self.initDeck()
		card := self.dealCards()
		self.setTrump(card)
	case NotBeat:
		self.dconn.fromDesk()
		self.takeCards()
	case Beat:
		self.desk = self.desk[:0]
		self.takeCards()
	}
	self.markInactive()
	self.setRoles(res)
	self.cardToBeat = ""
}

// event handlers
// event handlers are actually transition functions.
// in case error event handler should neither change the gameState,
// nor return the state value different from passed to it as an argument.

func (self *gameState) handleStartInCollection(s sm.State, e *sm.Event) sm.State {
	if n := self.hub.conns.(*mapRing).Len(); n < 2 || n > 6 {
		logWrongNumber(n)
		return s
	}

	self.newRound(None)
	return stateAttack
}

func (self *gameState) handleMoveInAttack(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.aconn {
		logOutOfTurn(conn)
		return s
	}

	// attacker sent the card
	if card != "" {
		// throw card to desk
		if _, ok := conn.cards[card]; !ok {
			logStrangeCard(card, conn)
			return s
		}
		conn.toDesk(card)

		self.cardToBeat = card
		return stateDefense
	}

	// attacker sent no card

	aconn := self.nextPlayer(self.aconn)
	if aconn == self.dconn {
		aconn = self.nextPlayer(aconn)
	}

	// check if all attackers have been polled
	if aconn == self.aconnStart {
		self.newRound(Beat)
		return stateAttack
	}

	self.aconn = aconn
	return stateAttack
}

func (self *gameState) handleMoveInDefense(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.dconn {
		logOutOfTurn(conn)
		return s
	}

	// defender takes the cards
	if card == "" {
		self.newRound(NotBeat)
		return stateAttack
	}

	// check that the sent card is capable to beat
	if higher(card, self.cardToBeat, self.trump) != 1 {
		logWontBeat(card, self.cardToBeat, self.trump)
		return s
	}

	// throw card to desk
	if _, ok := conn.cards[card]; !ok {
		logStrangeCard(card, conn)
		return s
	}
	conn.toDesk(card)

	return stateAttack
}

func (self *gameState) showDesk(s sm.State, e *sm.Event) sm.State {
	desk, err := json.Marshal(DeskMsg{self.desk})
	if err != nil {
		log.Println(err)
		return s
	}

	self.hub.bcastChan <- desk

	return s
}

func (self *gameState) log(s sm.State, e *sm.Event) sm.State {
	log.Println(self)
	return s
}

//

func NewGameState() *gameState {
	gst := new(gameState)

	gst.sm = sm.New(stateCollection, uint(stateCount), uint(cmdCount))

	gst.sm.OnChain(cmdStart,
		[]sm.State{stateCollection},
		[]sm.EventHandler{
			gst.handleStartInCollection,
			gst.showDesk,
			gst.log,
		},
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack},
		gst.handleMoveInAttack,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateDefense},
		gst.handleMoveInDefense,
	)

	gst.sm.OnChain(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		[]sm.EventHandler{
			gst.showDesk,
			gst.log,
		},
	)

	gst.deck = make([]string, 0, len(Suits)*len(CardValues)+ /*for trump*/ 1)
	gst.desk = make([]string, 0, 12)

	gst.hub = NewHub()

	return gst
}

//

type playerConn struct {
	gst *gameState

	cards  map[string]struct{}
	active bool

	conn      *websocket.Conn
	hubToConn chan []byte
}

func (self *playerConn) fromDeck() (card string) {
	if card = self.gst.popCard(); card != "" {
		self.cards[card] = struct{}{}
	}
	return
}

func (self *playerConn) toDesk(card string) {
	delete(self.cards, card)

	self.gst.desk = append(self.gst.desk, card)
}

func (self *playerConn) fromDesk() {
	for _, card := range self.gst.desk {
		self.cards[card] = struct{}{}
	}

	self.gst.desk = self.gst.desk[:0]
}

func (self *playerConn) write() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		select {
		case m, ok := <-self.hubToConn:
			if !ok {
				return
			}
			//TODO: text or binary?
			err := self.conn.WriteMessage(websocket.TextMessage, m)
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (self *playerConn) read() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		var m PlayerMsg

		err := self.conn.ReadJSON(&m)
		if err != nil {
			log.Println(err)
			return
		}

		var event *sm.Event
		switch m.Cmd {
		case cmdStart:
			event = &sm.Event{cmdStart, nil}
		case cmdMove:
			event = &sm.Event{cmdMove, cmdArgs{self, m.Card}}
		default:
			log.Printf("unknown command: %v", m.Cmd)
			continue
		}

		err = self.gst.sm.Emit(event)
		if err != nil {
			log.Println(err)
		}
	}
}

//

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

func playerHandler(gst *gameState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}

		p := &playerConn{gst, make(map[string]struct{}), true, conn, make(chan []byte)}

		gst.hub.regChan <- p
		defer func() {
			gst.hub.unregChan <- p
		}()

		go p.write()
		p.read()
	}
}

//

func startDurakSrv() error {
	gst := NewGameState()

	http.HandleFunc("/", playerHandler(gst))

	return http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func main() {
	err := startDurakSrv()
	if err != nil {
		log.Fatal(err)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())

	runtime.GOMAXPROCS(runtime.NumCPU())
}
<|endoftext|>


<commit_before>package gaestatic

import (
	"strings"
	"fmt"
	"strconv"
	"net/http"
	"text/template"
	"bytes"
	"net/url"
)

const PLIST_TEMPLATE string = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>items</key>
		<array>
			<dict>
				<key>assets</key>
				<array>
					{{if .IpaUrl}}
					<dict>
						<key>kind</key>
						<string>software-package</string>
						<key>url</key>
						<string>{{.IpaUrl}}</string>
					</dict>
					{{end}}
					{{if .DisplayImageUrl}}
					<dict>
						<key>kind</key>
						<string>display-image</string>
						<key>url</key>
						<string>{{.DisplayImageUrl}}</string>
					</dict>
					{{end}}
					{{if .FullSizeImageUrl}}
					<dict>
						<key>kind</key>
						<string>full-size-image</string>
						<key>url</key>
						<string>{{.FullSizeImageUrl}}</string>
					</dict>
					{{end}}
				</array>
				<key>metadata</key>
				<dict>
					{{if .BundleIdentifer}}
					<key>bundle-identifier</key>
					<string>{{.BundleIdentifer}}</string>
					{{end}}
					{{if .BundleVersion}}
					<key>bundle-version</key>
					<string>{{.BundleVersion}}</string>
					<key>kind</key>
					{{end}}
					{{if .Title}}
					<string>software</string>
					<key>title</key>
					<string>{{.Title}}</string>
					{{end}}
				</dict>
			</dict>
		</array>
	</dict>
</plist>
`

type PlistTemplateParams struct {
	// eg. https://example.com/apps/ios/sample.ipa
	IpaUrl string
	// eg. https://example.com/apps/ios/image.png
	DisplayImageUrl string
	// eg. https://example.com/apps/ios/full-image.png
	FullSizeImageUrl string
	// eg. com.example.sample
	BundleIdentifer string
	// eg. 1.0
	BundleVersion string
	// eg. Sample App
	Title string
}

/**
 * Dynamic Plist Handler
 */
func plistHandler(w http.ResponseWriter, r *http.Request) bool {

	isDone := true

	config := GetAppConfig()
	if config == nil {
		// Internal Server Errror
		w.WriteHeader(500)
		w.Write([]byte("No Config"))
		return isDone
	}

	filePath := strings.Replace(r.URL.Path, config.PlistDir, "", 1)
	tmp := strings.SplitN(filePath, "/", 3)
	if len(tmp) < 3 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #1"))
		return isDone
	}
	if tmp[2] != "x.plist" {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #2"))
		return isDone
	}
	bundleIdentifer := tmp[0]
	if strings.Contains(tmp[1], "..") {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #3"))
		return isDone
	}

	ipaUrl, _ := url.Parse(r.RequestURI)
	if !r.URL.IsAbs() {
		ipaUrl.Scheme = "https"
		ipaUrl.Host = r.Host
	}
	ipaUrl.Path = "/" + tmp[1]

	params := PlistTemplateParams{}
	// http://example.com/{filePath}/{bundleId}/{IpaPath}?title={title}&version={bundleVersion}
	params.Title = r.URL.Query().Get("title")
	params.BundleVersion = r.URL.Query().Get("version")
	params.BundleIdentifer = bundleIdentifer
	params.IpaUrl = ipaUrl.String()

	tmpl, err := template.New("plist").Parse(PLIST_TEMPLATE)

	if err != nil {
		// Not Found
		w.WriteHeader(501)
		w.Write([]byte(fmt.Sprintf("plist template is invalid.")))
		return isDone
	}

	writer := new(bytes.Buffer)
	err	= tmpl.Execute(writer, params)

	var contentLength string
	if err != nil {
		// Forbidden : サイズ取得失敗
		w.WriteHeader(403)
		w.Write([]byte(fmt.Sprintf("plist params is invalid.")))
		return isDone
	} else {
		contentLength = strconv.FormatInt(int64(writer.Len()), 10)
	}
	contentLength = contentLength + "bytes"

	contentType := GetContentType("_.plist")
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.Write(writer.Bytes())
	isDone = true
	return isDone
}
<commit_msg>dynamic plist : snapshot<commit_after>package gaestatic

import (
	"strings"
	"fmt"
	"strconv"
	"net/http"
	"text/template"
	"bytes"
	"net/url"
)

const PLIST_TEMPLATE string = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
	<dict>
		<key>items</key>
		<array>
			<dict>
				<key>assets</key>
				<array>
					{{if .IpaUrl}}
					<dict>
						<key>kind</key>
						<string>software-package</string>
						<key>url</key>
						<string>{{.IpaUrl}}</string>
					</dict>
					{{end}}
					{{if .DisplayImageUrl}}
					<dict>
						<key>kind</key>
						<string>display-image</string>
						<key>url</key>
						<string>{{.DisplayImageUrl}}</string>
					</dict>
					{{end}}
					{{if .FullSizeImageUrl}}
					<dict>
						<key>kind</key>
						<string>full-size-image</string>
						<key>url</key>
						<string>{{.FullSizeImageUrl}}</string>
					</dict>
					{{end}}
				</array>
				<key>metadata</key>
				<dict>
					{{if .BundleIdentifer}}
					<key>bundle-identifier</key>
					<string>{{.BundleIdentifer}}</string>
					{{end}}
					{{if .BundleVersion}}
					<key>bundle-version</key>
					<string>{{.BundleVersion}}</string>
					<key>kind</key>
					{{end}}
					{{if .Title}}
					<string>software</string>
					<key>title</key>
					<string>{{.Title}}</string>
					{{end}}
				</dict>
			</dict>
		</array>
	</dict>
</plist>
`

type PlistTemplateParams struct {
	// eg. https://example.com/apps/ios/sample.ipa
	IpaUrl string
	// eg. https://example.com/apps/ios/image.png
	DisplayImageUrl string
	// eg. https://example.com/apps/ios/full-image.png
	FullSizeImageUrl string
	// eg. com.example.sample
	BundleIdentifer string
	// eg. 1.0
	BundleVersion string
	// eg. Sample App
	Title string
}

/**
 * Dynamic Plist Handler
 */
func plistHandler(w http.ResponseWriter, r *http.Request) bool {
	const DYNAMIC_PLIST_POSTFIX = "x.plist"
	isDone := true

	config := GetAppConfig()
	if config == nil {
		// Internal Server Errror
		w.WriteHeader(500)
		w.Write([]byte("No Config"))
		return isDone
	}

	filePath := strings.Replace(r.URL.Path, config.PlistDir, "", 1)
	tmp := strings.SplitN(filePath, "/", 2)
	if len(tmp) < 2 {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #1"))
		return isDone
	}
	filePath = tmp[1]
	postfix := filePath[strings.LastIndex(filePath, DYNAMIC_PLIST_POSTFIX):]
	if postfix != DYNAMIC_PLIST_POSTFIX {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #2"))
		return isDone
	}

	filePath = filePath[0:len(filePath)-len(DYNAMIC_PLIST_POSTFIX)]
	tmp = strings.SplitN(filePath, "/", 2)
	bundleIdentifer := tmp[0]
	if strings.Contains(tmp[1], "..") {
		// Bad Request
		w.WriteHeader(400)
		w.Write([]byte("invalid path #3"))
		return isDone
	}

	ipaUrl, _ := url.Parse(r.RequestURI)
	if !r.URL.IsAbs() {
		ipaUrl.Scheme = "https"
		ipaUrl.Host = r.Host
	}
	ipaUrl.Path = "/" + tmp[1]

	params := PlistTemplateParams{}
	// http://example.com/{filePath}/{bundleId}/{IpaPath}?title={title}&version={bundleVersion}
	params.Title = r.URL.Query().Get("title")
	params.BundleVersion = r.URL.Query().Get("version")
	params.BundleIdentifer = bundleIdentifer
	params.IpaUrl = ipaUrl.String()

	tmpl, err := template.New("plist").Parse(PLIST_TEMPLATE)

	if err != nil {
		// Not Found
		w.WriteHeader(501)
		w.Write([]byte(fmt.Sprintf("plist template is invalid.")))
		return isDone
	}

	writer := new(bytes.Buffer)
	err	= tmpl.Execute(writer, params)

	var contentLength string
	if err != nil {
		// Forbidden : サイズ取得失敗
		w.WriteHeader(403)
		w.Write([]byte(fmt.Sprintf("plist params is invalid.")))
		return isDone
	} else {
		contentLength = strconv.FormatInt(int64(writer.Len()), 10)
	}
	contentLength = contentLength + "bytes"

	contentType := GetContentType("_.plist")
	if contentType != "" {
		w.Header().Set("Content-Type", contentType)
	}
	w.Write(writer.Bytes())
	isDone = true
	return isDone
}
<|endoftext|>


<commit_before>package elasticthought

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

	"github.com/couchbaselabs/cbfs/client"
	"github.com/couchbaselabs/logg"
)

func EnvironmentSanityCheck(config Configuration) error {

	if err := CbfsSanityCheck(config); err != nil {
		return err
	}
	logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check passed")

	return nil

}

func CbfsReadWriteFile(config Configuration, destPath, content string) error {

	// get cbfs client
	// Create a cbfs client
	cbfs, err := cbfsclient.New(config.CbfsUrl)
	if err != nil {
		return err
	}

	// write to random cbfs file
	options := cbfsclient.PutOptions{
		ContentType: "text/plain",
	}

	buffer := bytes.NewBuffer([]byte(content))

	if err := cbfs.Put("", destPath, buffer, options); err != nil {
		return fmt.Errorf("Error writing %v to cbfs: %v", destPath, err)
	}

	// read contents from cbfs file
	reader, err := cbfs.Get(destPath)
	if err != nil {
		return err
	}
	defer reader.Close()

	bytes, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}

	if string(bytes) != content {
		return fmt.Errorf("Content did not match expected")
	}

	// delete contents on cbfs
	if err := cbfs.Rm(destPath); err != nil {
		return err
	}
	return nil

}

func CbfsSanityCheck(config Configuration) error {

	uuid := NewUuid() // use uuid so other nodes on cluster don't conflict
	numAttempts := 10
	for i := 0; i < numAttempts; i++ {
		filename := fmt.Sprintf("env_check_%v_%v", uuid, i)
		content := fmt.Sprintf("Hello %v_%v", uuid, i)
		err := CbfsReadWriteFile(config, filename, content)
		if err == nil {
			return nil
		}
		logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity failed attempt # %s", i)
		if i >= (numAttempts - 1) {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check giving up")
			return err
		} else {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check sleeping ..")
			time.Sleep(time.Duration(i) * time.Second)
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check done sleeping")
		}
	}
	return fmt.Errorf("Exhausted attempts")

}
<commit_msg>string printf issue<commit_after>package elasticthought

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

	"github.com/couchbaselabs/cbfs/client"
	"github.com/couchbaselabs/logg"
)

func EnvironmentSanityCheck(config Configuration) error {

	if err := CbfsSanityCheck(config); err != nil {
		return err
	}
	logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check passed")

	return nil

}

func CbfsReadWriteFile(config Configuration, destPath, content string) error {

	// get cbfs client
	// Create a cbfs client
	cbfs, err := cbfsclient.New(config.CbfsUrl)
	if err != nil {
		return err
	}

	// write to random cbfs file
	options := cbfsclient.PutOptions{
		ContentType: "text/plain",
	}

	buffer := bytes.NewBuffer([]byte(content))

	if err := cbfs.Put("", destPath, buffer, options); err != nil {
		return fmt.Errorf("Error writing %v to cbfs: %v", destPath, err)
	}

	// read contents from cbfs file
	reader, err := cbfs.Get(destPath)
	if err != nil {
		return err
	}
	defer reader.Close()

	bytes, err := ioutil.ReadAll(reader)
	if err != nil {
		return err
	}

	if string(bytes) != content {
		return fmt.Errorf("Content did not match expected")
	}

	// delete contents on cbfs
	if err := cbfs.Rm(destPath); err != nil {
		return err
	}
	return nil

}

func CbfsSanityCheck(config Configuration) error {

	uuid := NewUuid() // use uuid so other nodes on cluster don't conflict
	numAttempts := 10
	for i := 0; i < numAttempts; i++ {
		filename := fmt.Sprintf("env_check_%v_%v", uuid, i)
		content := fmt.Sprintf("Hello %v_%v", uuid, i)
		err := CbfsReadWriteFile(config, filename, content)
		if err == nil {
			return nil
		}
		logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity failed attempt # %v", i)
		if i >= (numAttempts - 1) {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check giving up")
			return err
		} else {
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check sleeping ..")
			time.Sleep(time.Duration(i) * time.Second)
			logg.LogTo("ELASTIC_THOUGHT", "Cbfs sanity check done sleeping")
		}
	}
	return fmt.Errorf("Exhausted attempts")

}
<|endoftext|>


<commit_before>// Copyright (c) - Damien Fontaine <damien.fontaine@lineolia.net>
//
// This program 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.
//
// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>

package mongo

import (
	"context"
	"fmt"
	"log"

	"github.com/mongodb/mongo-go-driver/mongo"
)

//Mongo is a datasource.
type Mongo struct {
	Client  *mongo.Client
	context context.Context
}

//NewMongo creates a newinstance of Mongo
func NewMongo(filename string, environment string) (*Mongo, error) {
	ctx := context.Background()
	cnf, err := GetMongo(filename, environment)
	if err != nil {
		return nil, err
	}
	var uri string
	if len(cnf.Username) > 0 && len(cnf.Password) > 0 {
		uri = fmt.Sprintf(`mongodb://%s:%s@%s:%d/%s`,
			cnf.Username,
			cnf.Password,
			cnf.Host,
			cnf.Port,
			cnf.Database,
		)
	} else {
		uri = fmt.Sprintf(`mongodb://%s:%d/%s`,
			cnf.Host,
			cnf.Port,
			cnf.Database,
		)
	}
	client, err := mongo.NewClient(uri)
	if err != nil {
		log.Printf("L'URI du serveur MongoDB est incorrect: %s", uri)
		return nil, err
	}
	client.Connect(ctx)
	if err != nil {
		log.Print("Impossible d'utiliser ce context")
		return nil, err
	}
	err = client.Ping(ctx, nil)
	if err != nil {
		log.Printf("Impossible de contacter %v sur le port %d", cnf.Host, cnf.Port)
		return nil, err
	}
	return &Mongo{Client: client, context: ctx}, nil
}

//Disconnect a Mongo client
func (m *Mongo) Disconnect() error {
	err := m.Client.Disconnect(m.context)
	if err != nil {
		log.Printf("Impossible de fermer la connexion")
		return err
	}
	return nil
}
<commit_msg>ListCollections to test connection in Mongo<commit_after>// Copyright (c) - Damien Fontaine <damien.fontaine@lineolia.net>
//
// This program 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.
//
// This program 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 this program.  If not, see <http://www.gnu.org/licenses/>

package mongo

import (
	"context"
	"fmt"
	"log"

	"github.com/mongodb/mongo-go-driver/mongo"
)

//Mongo is a datasource.
type Mongo struct {
	Client  *mongo.Client
	context context.Context
}

//NewMongo creates a newinstance of Mongo
func NewMongo(filename string, environment string) (*Mongo, error) {
	ctx := context.Background()
	cnf, err := GetMongo(filename, environment)
	if err != nil {
		return nil, err
	}
	var uri string
	if len(cnf.Username) > 0 && len(cnf.Password) > 0 {
		uri = fmt.Sprintf(`mongodb://%s:%s@%s:%d/%s`,
			cnf.Username,
			cnf.Password,
			cnf.Host,
			cnf.Port,
			cnf.Database,
		)
	} else {
		uri = fmt.Sprintf(`mongodb://%s:%d/%s`,
			cnf.Host,
			cnf.Port,
			cnf.Database,
		)
	}
	client, err := mongo.NewClient(uri)
	if err != nil {
		log.Printf("L'URI du serveur MongoDB est incorrect: %s", uri)
		return nil, err
	}
	err = client.Connect(ctx)
	if err != nil {
		log.Print("Impossible d'utiliser ce context")
		return nil, err
	}

	db := client.Database(cnf.Database)
	_, err = db.ListCollections(ctx, nil, nil)
	if err != nil {
		log.Printf("%v", err)
		return nil, err
	}

	err = client.Ping(ctx, nil)
	if err != nil {
		log.Printf("Impossible de contacter %v sur le port %d", cnf.Host, cnf.Port)
		return nil, err
	}
	return &Mongo{Client: client, context: ctx}, nil
}

//Disconnect a Mongo client
func (m *Mongo) Disconnect() error {
	err := m.Client.Disconnect(m.context)
	if err != nil {
		log.Printf("Impossible de fermer la connexion")
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Copyright 2014 Ninja Blocks Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rpc

import (
	"fmt"
	"reflect"
	"sync"
	"unicode"
	"unicode/utf8"

	"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
	"github.com/davecgh/go-spew/spew"
)

var (
	// Precompute the reflect.Type of error and mqtt.Message
	typeOfError   = reflect.TypeOf((*error)(nil)).Elem()
	typeOfRequest = reflect.TypeOf((*mqtt.Message)(nil)).Elem()
)

// ----------------------------------------------------------------------------
// service
// ----------------------------------------------------------------------------

type service struct {
	name     string                    // name of service
	rcvr     reflect.Value             // receiver of methods for the service
	rcvrType reflect.Type              // type of the receiver
	methods  map[string]*serviceMethod // registered methods
}

type serviceMethod struct {
	method    reflect.Method // receiver method
	argsType  reflect.Type   // type of the request argument
	replyType reflect.Type   // type of the response argument
}

// ----------------------------------------------------------------------------
// serviceMap
// ----------------------------------------------------------------------------

// serviceMap is a registry for services.
type serviceMap struct {
	mutex    sync.Mutex
	services map[string]*service
}

// register adds a new service using reflection to extract its methods.
func (m *serviceMap) register(rcvr interface{}, name string) (methods []string, err error) {

	log.Infof("ADDING SERVICE '%s'", name)

	// Setup service.
	s := &service{
		name:     name,
		rcvr:     reflect.ValueOf(rcvr),
		rcvrType: reflect.TypeOf(rcvr),
		methods:  make(map[string]*serviceMethod),
	}
	if name == "" {
		s.name = reflect.Indirect(s.rcvr).Type().Name()
		if !isExported(s.name) {
			return nil, fmt.Errorf("rpc: type %q is not exported", s.name)
		}
	}
	if s.name == "" {
		return nil, fmt.Errorf("rpc: no service name for type %q", s.rcvrType.String())
	}
	// Setup methods.
	for i := 0; i < s.rcvrType.NumMethod(); i++ {
		method := s.rcvrType.Method(i)
		mtype := method.Type
		// Method must be exported.
		if method.PkgPath != "" {
			continue
		}
		// Method needs four ins: receiver, *mqtt.Message, *args, *reply.
		if mtype.NumIn() != 4 {
			continue
		}
		// First argument must be a pointer and must be mqtt.Message.
		reqType := mtype.In(1)
		if reqType != typeOfRequest {
			continue
		}
		// Second argument must be a pointer and must be exported.
		args := mtype.In(2)
		if args.Kind() != reflect.Ptr || !isExportedOrBuiltin(args) {
			continue
		}
		// Third argument must be a pointer and must be exported.
		reply := mtype.In(3)
		if reply.Kind() != reflect.Ptr || !isExportedOrBuiltin(reply) {
			continue
		}
		// Method needs one out: error.
		if mtype.NumOut() != 1 {
			continue
		}
		if returnType := mtype.Out(0); returnType != typeOfError {
			continue
		}
		s.methods[method.Name] = &serviceMethod{
			method:    method,
			argsType:  args.Elem(),
			replyType: reply.Elem(),
		}
	}
	if len(s.methods) == 0 {
		return nil, fmt.Errorf("rpc: %q has no exported methods of suitable type", s.name)
	}
	// Add to the map.
	m.mutex.Lock()
	defer m.mutex.Unlock()
	if m.services == nil {
		m.services = make(map[string]*service)
	} else if _, ok := m.services[s.name]; ok {
		return nil, fmt.Errorf("rpc: service already defined: %q", s.name)
	}
	m.services[s.name] = s

	exportedMethods := make([]string, len(s.methods))
	i := 0
	for name := range s.methods {
		exportedMethods[i] = name
		i++
	}
	return exportedMethods, nil
}

// get returns a registered service given a method name.
func (m *serviceMap) get(topic string, method string) (*service, *serviceMethod, error) {
	m.mutex.Lock()
	service := m.services[topic]
	spew.Dump(m.services)
	m.mutex.Unlock()
	if service == nil {
		err := fmt.Errorf("rpc: can't find service %q", topic)
		return nil, nil, err
	}
	serviceMethod := service.methods[method]
	if serviceMethod == nil {
		err := fmt.Errorf("rpc: can't find method %q", method)
		return nil, nil, err
	}
	return service, serviceMethod, nil
}

// isExported returns true of a string is an exported (upper case) name.
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// isExportedOrBuiltin returns true if a type is exported or a builtin.
func isExportedOrBuiltin(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}
<commit_msg>Clean up logging<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2012 The Gorilla Authors. All rights reserved.
// Copyright 2014 Ninja Blocks Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rpc

import (
	"fmt"
	"reflect"
	"sync"
	"unicode"
	"unicode/utf8"

	"git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.golang.git"
)

var (
	// Precompute the reflect.Type of error and mqtt.Message
	typeOfError   = reflect.TypeOf((*error)(nil)).Elem()
	typeOfRequest = reflect.TypeOf((*mqtt.Message)(nil)).Elem()
)

// ----------------------------------------------------------------------------
// service
// ----------------------------------------------------------------------------

type service struct {
	name     string                    // name of service
	rcvr     reflect.Value             // receiver of methods for the service
	rcvrType reflect.Type              // type of the receiver
	methods  map[string]*serviceMethod // registered methods
}

type serviceMethod struct {
	method    reflect.Method // receiver method
	argsType  reflect.Type   // type of the request argument
	replyType reflect.Type   // type of the response argument
}

// ----------------------------------------------------------------------------
// serviceMap
// ----------------------------------------------------------------------------

// serviceMap is a registry for services.
type serviceMap struct {
	mutex    sync.Mutex
	services map[string]*service
}

// register adds a new service using reflection to extract its methods.
func (m *serviceMap) register(rcvr interface{}, name string) (methods []string, err error) {

	// Setup service.
	s := &service{
		name:     name,
		rcvr:     reflect.ValueOf(rcvr),
		rcvrType: reflect.TypeOf(rcvr),
		methods:  make(map[string]*serviceMethod),
	}
	if name == "" {
		s.name = reflect.Indirect(s.rcvr).Type().Name()
		if !isExported(s.name) {
			return nil, fmt.Errorf("rpc: type %q is not exported", s.name)
		}
	}
	if s.name == "" {
		return nil, fmt.Errorf("rpc: no service name for type %q", s.rcvrType.String())
	}
	// Setup methods.
	for i := 0; i < s.rcvrType.NumMethod(); i++ {
		method := s.rcvrType.Method(i)
		mtype := method.Type
		// Method must be exported.
		if method.PkgPath != "" {
			continue
		}
		// Method needs four ins: receiver, *mqtt.Message, *args, *reply.
		if mtype.NumIn() != 4 {
			continue
		}
		// First argument must be a pointer and must be mqtt.Message.
		reqType := mtype.In(1)
		if reqType != typeOfRequest {
			continue
		}
		// Second argument must be a pointer and must be exported.
		args := mtype.In(2)
		if args.Kind() != reflect.Ptr || !isExportedOrBuiltin(args) {
			continue
		}
		// Third argument must be a pointer and must be exported.
		reply := mtype.In(3)
		if reply.Kind() != reflect.Ptr || !isExportedOrBuiltin(reply) {
			continue
		}
		// Method needs one out: error.
		if mtype.NumOut() != 1 {
			continue
		}
		if returnType := mtype.Out(0); returnType != typeOfError {
			continue
		}
		s.methods[method.Name] = &serviceMethod{
			method:    method,
			argsType:  args.Elem(),
			replyType: reply.Elem(),
		}
	}
	if len(s.methods) == 0 {
		return nil, fmt.Errorf("rpc: %q has no exported methods of suitable type", s.name)
	}
	// Add to the map.
	m.mutex.Lock()
	defer m.mutex.Unlock()
	if m.services == nil {
		m.services = make(map[string]*service)
	} else if _, ok := m.services[s.name]; ok {
		return nil, fmt.Errorf("rpc: service already defined: %q", s.name)
	}
	m.services[s.name] = s

	exportedMethods := make([]string, len(s.methods))
	i := 0
	for name := range s.methods {
		exportedMethods[i] = name
		i++
	}
	return exportedMethods, nil
}

// get returns a registered service given a method name.
func (m *serviceMap) get(topic string, method string) (*service, *serviceMethod, error) {
	m.mutex.Lock()
	service := m.services[topic]
	m.mutex.Unlock()
	if service == nil {
		err := fmt.Errorf("rpc: can't find service %q", topic)
		return nil, nil, err
	}
	serviceMethod := service.methods[method]
	if serviceMethod == nil {
		err := fmt.Errorf("rpc: can't find method %q", method)
		return nil, nil, err
	}
	return service, serviceMethod, nil
}

// isExported returns true of a string is an exported (upper case) name.
func isExported(name string) bool {
	rune, _ := utf8.DecodeRuneInString(name)
	return unicode.IsUpper(rune)
}

// isExportedOrBuiltin returns true if a type is exported or a builtin.
func isExportedOrBuiltin(t reflect.Type) bool {
	for t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(t.Name()) || t.PkgPath() == ""
}
<|endoftext|>


<commit_before>package errors

import (
	"crypto/x509"
	"encoding/json"
	"errors"
	"testing"
)

func TestNew(t *testing.T) {
	err := New(CertificateError, Unknown)
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(CertificateError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != "Unknown certificate error" {
		t.Fatal("Error message construction failed.")
	}

	code := New(OCSPError, ReadFailed).ErrorCode
	if code != 8001 {
		t.Fatal("Improper error code")
	}

	code = New(OCSPError, IssuerMismatch).ErrorCode
	if code != 8100 {
		t.Fatal("Improper error code")
	}

	code = New(OCSPError, InvalidStatus).ErrorCode
	if code != 8200 {
		t.Fatal("Improper error code")
	}

	code = New(CertificateError, Unknown).ErrorCode
	if code != 1000 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, ReadFailed).ErrorCode
	if code != 1001 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, DecodeFailed).ErrorCode
	if code != 1002 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, ParseFailed).ErrorCode
	if code != 1003 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, SelfSigned).ErrorCode
	if code != 1100 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, VerifyFailed).ErrorCode
	if code != 1200 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, BadRequest).ErrorCode
	if code != 1300 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, MissingSerial).ErrorCode
	if code != 1400 {
		t.Fatal("Improper error code")
	}

	code = New(PrivateKeyError, Unknown).ErrorCode
	if code != 2000 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, ReadFailed).ErrorCode
	if code != 2001 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, DecodeFailed).ErrorCode
	if code != 2002 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, ParseFailed).ErrorCode
	if code != 2003 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, Encrypted).ErrorCode
	if code != 2100 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, NotRSAOrECC).ErrorCode
	if code != 2200 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, KeyMismatch).ErrorCode
	if code != 2300 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, GenerationFailed).ErrorCode
	if code != 2400 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, Unavailable).ErrorCode
	if code != 2500 {
		t.Fatal("Improper error code")
	}

	code = New(IntermediatesError, Unknown).ErrorCode
	if code != 3000 {
		t.Fatal("Improper error code")
	}
	code = New(IntermediatesError, ReadFailed).ErrorCode
	if code != 3001 {
		t.Fatal("Improper error code")
	}
	code = New(IntermediatesError, DecodeFailed).ErrorCode
	if code != 3002 {
		t.Fatal("Improper error code")
	}
	code = New(IntermediatesError, ParseFailed).ErrorCode
	if code != 3003 {
		t.Fatal("Improper error code")
	}

	code = New(RootError, Unknown).ErrorCode
	if code != 4000 {
		t.Fatal("Improper error code")
	}
	code = New(RootError, ReadFailed).ErrorCode
	if code != 4001 {
		t.Fatal("Improper error code")
	}
	code = New(RootError, DecodeFailed).ErrorCode
	if code != 4002 {
		t.Fatal("Improper error code")
	}
	code = New(RootError, ParseFailed).ErrorCode
	if code != 4003 {
		t.Fatal("Improper error code")
	}

	code = New(PolicyError, Unknown).ErrorCode
	if code != 5000 {
		t.Fatal("Improper error code")
	}
	code = New(PolicyError, NoKeyUsages).ErrorCode
	if code != 5100 {
		t.Fatal("Improper error code")
	}
	code = New(PolicyError, InvalidPolicy).ErrorCode
	if code != 5200 {
		t.Fatal("Improper error code")
	}
	code = New(PolicyError, InvalidRequest).ErrorCode
	if code != 5300 {
		t.Fatal("Improper error code")
	}

	code = New(DialError, Unknown).ErrorCode
	if code != 6000 {
		t.Fatal("Improper error code")
	}

	code = New(APIClientError, AuthenticationFailure).ErrorCode
	if code != 7100 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, JSONError).ErrorCode
	if code != 7200 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, ClientHTTPError).ErrorCode
	if code != 7400 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, IOError).ErrorCode
	if code != 7300 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, ServerRequestFailed).ErrorCode
	if code != 7500 {
		t.Fatal("Improper error code")
	}

	code = New(CSRError, Unknown).ErrorCode
	if code != 9000 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, ReadFailed).ErrorCode
	if code != 9001 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, DecodeFailed).ErrorCode
	if code != 9002 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, ParseFailed).ErrorCode
	if code != 9003 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, KeyMismatch).ErrorCode
	if code != 9300 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, BadRequest).ErrorCode
	if code != 9300 {
		t.Fatal("Improper error code")
	}
}

func TestWrap(t *testing.T) {
	msg := "Arbitrary error message"
	err := Wrap(CertificateError, Unknown, errors.New(msg))
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(CertificateError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != msg {
		t.Fatal("Error message construction failed.")
	}

	err = Wrap(CertificateError, VerifyFailed, x509.CertificateInvalidError{Reason: x509.Expired})
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(CertificateError)+int(VerifyFailed)+certificateInvalid+int(x509.Expired) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != "x509: certificate has expired or is not yet valid" {
		t.Fatal("Error message construction failed.")
	}
}

func TestMarshal(t *testing.T) {
	msg := "Arbitrary error message"
	err := Wrap(CertificateError, Unknown, errors.New(msg))
	bytes, _ := json.Marshal(err)
	var received Error
	json.Unmarshal(bytes, &received)
	if received.ErrorCode != int(CertificateError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if received.Message != msg {
		t.Fatal("Error message construction failed.")
	}
}

func TestErrorString(t *testing.T) {
	msg := "Arbitrary error message"
	err := Wrap(CertificateError, Unknown, errors.New(msg))
	str := err.Error()
	if str != `{"code":1000,"message":"`+msg+`"}` {
		t.Fatal("Incorrect Error():", str)
	}
}
<commit_msg>error package code coverage<commit_after>package errors

import (
	"crypto/x509"
	"encoding/json"
	"errors"
	"testing"
)

func TestNew(t *testing.T) {
	err := New(CertificateError, Unknown)
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(CertificateError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != "Unknown certificate error" {
		t.Fatal("Error message construction failed.")
	}

	code := New(OCSPError, ReadFailed).ErrorCode
	if code != 8001 {
		t.Fatal("Improper error code")
	}

	code = New(OCSPError, IssuerMismatch).ErrorCode
	if code != 8100 {
		t.Fatal("Improper error code")
	}

	code = New(OCSPError, InvalidStatus).ErrorCode
	if code != 8200 {
		t.Fatal("Improper error code")
	}

	code = New(CertificateError, Unknown).ErrorCode
	if code != 1000 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, ReadFailed).ErrorCode
	if code != 1001 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, DecodeFailed).ErrorCode
	if code != 1002 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, ParseFailed).ErrorCode
	if code != 1003 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, SelfSigned).ErrorCode
	if code != 1100 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, VerifyFailed).ErrorCode
	if code != 1200 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, BadRequest).ErrorCode
	if code != 1300 {
		t.Fatal("Improper error code")
	}
	code = New(CertificateError, MissingSerial).ErrorCode
	if code != 1400 {
		t.Fatal("Improper error code")
	}

	code = New(PrivateKeyError, Unknown).ErrorCode
	if code != 2000 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, ReadFailed).ErrorCode
	if code != 2001 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, DecodeFailed).ErrorCode
	if code != 2002 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, ParseFailed).ErrorCode
	if code != 2003 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, Encrypted).ErrorCode
	if code != 2100 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, NotRSAOrECC).ErrorCode
	if code != 2200 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, KeyMismatch).ErrorCode
	if code != 2300 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, GenerationFailed).ErrorCode
	if code != 2400 {
		t.Fatal("Improper error code")
	}
	code = New(PrivateKeyError, Unavailable).ErrorCode
	if code != 2500 {
		t.Fatal("Improper error code")
	}

	code = New(IntermediatesError, Unknown).ErrorCode
	if code != 3000 {
		t.Fatal("Improper error code")
	}
	code = New(IntermediatesError, ReadFailed).ErrorCode
	if code != 3001 {
		t.Fatal("Improper error code")
	}
	code = New(IntermediatesError, DecodeFailed).ErrorCode
	if code != 3002 {
		t.Fatal("Improper error code")
	}
	code = New(IntermediatesError, ParseFailed).ErrorCode
	if code != 3003 {
		t.Fatal("Improper error code")
	}

	code = New(RootError, Unknown).ErrorCode
	if code != 4000 {
		t.Fatal("Improper error code")
	}
	code = New(RootError, ReadFailed).ErrorCode
	if code != 4001 {
		t.Fatal("Improper error code")
	}
	code = New(RootError, DecodeFailed).ErrorCode
	if code != 4002 {
		t.Fatal("Improper error code")
	}
	code = New(RootError, ParseFailed).ErrorCode
	if code != 4003 {
		t.Fatal("Improper error code")
	}

	code = New(PolicyError, Unknown).ErrorCode
	if code != 5000 {
		t.Fatal("Improper error code")
	}
	code = New(PolicyError, NoKeyUsages).ErrorCode
	if code != 5100 {
		t.Fatal("Improper error code")
	}
	code = New(PolicyError, InvalidPolicy).ErrorCode
	if code != 5200 {
		t.Fatal("Improper error code")
	}
	code = New(PolicyError, InvalidRequest).ErrorCode
	if code != 5300 {
		t.Fatal("Improper error code")
	}

	code = New(DialError, Unknown).ErrorCode
	if code != 6000 {
		t.Fatal("Improper error code")
	}

	code = New(APIClientError, AuthenticationFailure).ErrorCode
	if code != 7100 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, JSONError).ErrorCode
	if code != 7200 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, ClientHTTPError).ErrorCode
	if code != 7400 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, IOError).ErrorCode
	if code != 7300 {
		t.Fatal("Improper error code")
	}
	code = New(APIClientError, ServerRequestFailed).ErrorCode
	if code != 7500 {
		t.Fatal("Improper error code")
	}

	code = New(CSRError, Unknown).ErrorCode
	if code != 9000 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, ReadFailed).ErrorCode
	if code != 9001 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, DecodeFailed).ErrorCode
	if code != 9002 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, ParseFailed).ErrorCode
	if code != 9003 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, KeyMismatch).ErrorCode
	if code != 9300 {
		t.Fatal("Improper error code")
	}
	code = New(CSRError, BadRequest).ErrorCode
	if code != 9300 {
		t.Fatal("Improper error code")
	}

	code = New(CTError, Unknown).ErrorCode
	if code != 10000 {
		t.Fatal("Improper error code")
	}
	code = New(CTError, PrecertSubmissionFailed).ErrorCode
	if code != 10100 {
		t.Fatal("Improper error code")
	}
}

func TestWrap(t *testing.T) {
	msg := "Arbitrary error message"
	err := Wrap(CertificateError, Unknown, errors.New(msg))
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(CertificateError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != msg {
		t.Fatal("Error message construction failed.")
	}

	err = Wrap(CertificateError, VerifyFailed, x509.CertificateInvalidError{Reason: x509.Expired})
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(CertificateError)+int(VerifyFailed)+certificateInvalid+int(x509.Expired) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != "x509: certificate has expired or is not yet valid" {
		t.Fatal("Error message construction failed.")
	}

	err = Wrap(CertificateError, VerifyFailed, x509.UnknownAuthorityError{})
	if err == nil {
		t.Fatal("Error creation failed.")
	}

	err = Wrap(RootError, Unknown, errors.New(msg))
	if err == nil {
		t.Fatal("Error creation failed.")
	}
	if err.ErrorCode != int(RootError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if err.Message != msg {
		t.Fatal("Error message construction failed.")
	}
}

func TestMarshal(t *testing.T) {
	msg := "Arbitrary error message"
	err := Wrap(CertificateError, Unknown, errors.New(msg))
	bytes, _ := json.Marshal(err)
	var received Error
	json.Unmarshal(bytes, &received)
	if received.ErrorCode != int(CertificateError)+int(Unknown) {
		t.Fatal("Error code construction failed.")
	}
	if received.Message != msg {
		t.Fatal("Error message construction failed.")
	}
}

func TestErrorString(t *testing.T) {
	msg := "Arbitrary error message"
	err := Wrap(CertificateError, Unknown, errors.New(msg))
	str := err.Error()
	if str != `{"code":1000,"message":"`+msg+`"}` {
		t.Fatal("Incorrect Error():", str)
	}
}

func TestHTTP(t *testing.T) {
	err := NewMethodNotAllowed("GET")
	if err == nil {
		t.Fatal("New Mathod Check failed")
	}

	err = NewBadRequest(errors.New("Bad Request"))
	if err == nil {
		t.Fatal("New Bad Request Check failed")
	}

	if err.StatusCode != 400 {
		t.Fatal("New Bad Request error code construction failed")
	}

	err = NewBadRequestString("Bad Request String")
	if err == nil {
		t.Fatal("New Bad Request String Check failed")
	}

	if err.StatusCode != 400 {
		t.Fatal("New Bad Request String error code construction failed")
	}

	err = NewBadRequestMissingParameter("Request Missing Parameter")
	if err == nil {
		t.Fatal("New Bad Request Missing Parameter Check failed")
	}

	if err.StatusCode != 400 {
		t.Fatal("New Bad Request Missing Parameter error code construction failed")
	}

	err = NewBadRequestUnwantedParameter("Unwanted Parameter Present In Request")
	if err == nil {
		t.Fatal("New Bad Request Unwanted Parameter Check failed")
	}

	if err.StatusCode != 400 {
		t.Fatal("New Bad Request Unwanted Parameter error code construction failed")
	}

}

func TestHTTPErrorString(t *testing.T) {
	method := "GET"
	err := NewMethodNotAllowed(method)
	str := err.Error()
	if str != `Method is not allowed:"`+method+`"` {
		t.Fatal("Incorrect Error():", str)
	}
}
<|endoftext|>


<commit_before>package metrics

import (
	gmetrics "github.com/armon/go-metrics"
	"github.com/bakins/go-metrics-map"
	"github.com/bakins/go-metrics-middleware"
	"github.com/mistifyio/mistify-operator-admin/config"
)

// Context contains information necessary to add time and count metrics
// to routes, show collected metrics, or emit custom metrics from within a route
type Context struct {
	Metrics    *gmetrics.Metrics
	Middleware *mmw.Middleware
	MapSink    *mapsink.MapSink
	StatsdSink *gmetrics.StatsdSink
}

// One context only
var context *Context
var contextLoaded = false

// LoadContext loads the context from config
func LoadContext() error {
	conf := config.Get()
	apiConfig := &conf.Metrics
	c, err := NewContext(apiConfig)
	if err != nil {
		return err
	}
	context = c
	contextLoaded = true
	return nil
}

// GetContext retrieves the context, loading if it has not yet
func GetContext() *Context {
	if !contextLoaded {
		_ = LoadContext()
	}
	return context
}

// NewContext creates a new context given a statsd address and service name
func NewContext(apiConfig *config.Metrics) (*Context, error) {
	// Use the loaded default if one is not provided
	if apiConfig == nil {
		conf := config.Get()
		apiConfig = &conf.Metrics
	}

	// Build the fanout sink, with statsd if required
	var mainSink *gmetrics.FanoutSink
	var statsdSink *gmetrics.StatsdSink
	var err error
	mapSink := mapsink.New()
	if apiConfig.StatsdAddress != "" {
		statsdSink, err = gmetrics.NewStatsdSink(apiConfig.StatsdAddress)
		if err != nil {
			return nil, err
		}
		mainSink = &gmetrics.FanoutSink{statsdSink, mapSink}
	} else {
		mainSink = &gmetrics.FanoutSink{mapSink}
	}

	// Build the metrics object
	cfg := gmetrics.DefaultConfig(apiConfig.ServiceName)
	cfg.EnableHostname = false
	metrics, err := gmetrics.New(cfg, mainSink)
	if err != nil {
		return nil, err
	}

	// Create the middleware and return everything
	mmw := mmw.New(metrics)
	return &Context{metrics, mmw, mapSink, statsdSink}, nil
}
<commit_msg>Update NewContext comment<commit_after>package metrics

import (
	gmetrics "github.com/armon/go-metrics"
	"github.com/bakins/go-metrics-map"
	"github.com/bakins/go-metrics-middleware"
	"github.com/mistifyio/mistify-operator-admin/config"
)

// Context contains information necessary to add time and count metrics
// to routes, show collected metrics, or emit custom metrics from within a route
type Context struct {
	Metrics    *gmetrics.Metrics
	Middleware *mmw.Middleware
	MapSink    *mapsink.MapSink
	StatsdSink *gmetrics.StatsdSink
}

// One context only
var context *Context
var contextLoaded = false

// LoadContext loads the context from config
func LoadContext() error {
	conf := config.Get()
	apiConfig := &conf.Metrics
	c, err := NewContext(apiConfig)
	if err != nil {
		return err
	}
	context = c
	contextLoaded = true
	return nil
}

// GetContext retrieves the context, loading if it has not yet
func GetContext() *Context {
	if !contextLoaded {
		_ = LoadContext()
	}
	return context
}

// NewContext creates a new context from the config
func NewContext(apiConfig *config.Metrics) (*Context, error) {
	// Use the loaded default if one is not provided
	if apiConfig == nil {
		conf := config.Get()
		apiConfig = &conf.Metrics
	}

	// Build the fanout sink, with statsd if required
	var mainSink *gmetrics.FanoutSink
	var statsdSink *gmetrics.StatsdSink
	var err error
	mapSink := mapsink.New()
	if apiConfig.StatsdAddress != "" {
		statsdSink, err = gmetrics.NewStatsdSink(apiConfig.StatsdAddress)
		if err != nil {
			return nil, err
		}
		mainSink = &gmetrics.FanoutSink{statsdSink, mapSink}
	} else {
		mainSink = &gmetrics.FanoutSink{mapSink}
	}

	// Build the metrics object
	cfg := gmetrics.DefaultConfig(apiConfig.ServiceName)
	cfg.EnableHostname = false
	metrics, err := gmetrics.New(cfg, mainSink)
	if err != nil {
		return nil, err
	}

	// Create the middleware and return everything
	mmw := mmw.New(metrics)
	return &Context{metrics, mmw, mapSink, statsdSink}, nil
}
<|endoftext|>