<commit_before>package pagerduty

import "net/http"

// IncidentsService type
type IncidentsService struct {
	client *Client
}

// Incident type
type Incident struct {
	ID                    string            `json:"id,omitempty"`
	IncidentNumber        int               `json:"incident_number,omitempty"`
	Status                string            `json:"status,omitempty"`
	CreatedOn             string            `json:"created_on,omitempty"`
	Summary               *IncidentSummary  `json:"trigger_summary_data,omitempty"`
	User                  *User             `json:"assigned_to_user,omitempty"`
	SService               *Service          `json:"service,omitempty"` // This is conflicting with the package name on assignment in test. Not sure of the soltuion
	EEscalationPolicy      *EscalationPolicy `json:"escalation_policy,omitempty"` // This is conflicting with the package name on assignment in test. Not sure of the soltuion
	HTMLURL               string            `json:"html_url,omitempty"`
	IncidentKey           string            `json:"incident_key,omitempty"`
	TriggerDetailsHTMLURL string            `json:"trigger_details_html_url,omitempty"`
	TriggerType           string            `json:"trigger_type,omitempty"`
	LastStatusChangeOn    string            `json:"last_status_change_on,omitempty"`
	LastStatusChangeBy    *User             `json:"last_status_change_by,omitempty"`
	NumberOfEscalations   int               `json:"number_of_escalations,omitempty"`
	ResolvedByUser        *User             `json:"resolved_by_user,omitempty"`
	AssignedToUser        *User             `json:"assigned_to_user,omitempty"`
	AssignedTo            []*User           `json:"assigned_to,omitempty"`
}

// Incidents is a list of incidents
type Incidents struct {
	Incidents []Incident
}

// IncidentSummary type
type IncidentSummary struct {
	Subject     string //`json:"subject,omitempty"`
	Description string //`json:"description,omitempty"`
}

// Get returns a single incident by id if found
func (s *IncidentsService) Get(id string) (*Incident, *http.Response, error) {
	incident := new(Incident)

	res, err := s.client.Get("incidents/"+id, incident)
	if err != nil {
		return nil, res, err
	}

	return incident, res, nil
}

// IncidentsOptions provides optional parameters to list requests
type IncidentsOptions struct {
	Status string `url:"status,omitempty"`
	SortBy string `url:"sort_by,omitempty"`
	Since  string `url:"since,omitempty"`
	Until  string `url:"until,omitempty"`
}

// List returns a list of incidents
func (s *IncidentsService) List(opt *IncidentsOptions) (Incidents, *http.Response, error) {
	var incidents Incidents
	u, err := addOptions("incidents", opt)
	if err != nil {
		return incidents, nil, err
	}

	res, err := s.client.Get(u, incidents)
	if err != nil {
		return incidents, res, err
	}

	return incidents, res, err
}
<commit_msg>Incidents now has pagination<commit_after>package pagerduty

import "net/http"

// IncidentsService type
type IncidentsService struct {
	client *Client
}

// Incident type
type Incident struct {
	ID                    string            `json:"id,omitempty"`
	IncidentNumber        int               `json:"incident_number,omitempty"`
	Status                string            `json:"status,omitempty"`
	CreatedOn             string            `json:"created_on,omitempty"`
	Summary               *IncidentSummary  `json:"trigger_summary_data,omitempty"`
	User                  *User             `json:"assigned_to_user,omitempty"`
	SService               *Service          `json:"service,omitempty"` // This is conflicting with the package name on assignment in test. Not sure of the soltuion
	EEscalationPolicy      *EscalationPolicy `json:"escalation_policy,omitempty"` // This is conflicting with the package name on assignment in test. Not sure of the soltuion
	HTMLURL               string            `json:"html_url,omitempty"`
	IncidentKey           string            `json:"incident_key,omitempty"`
	TriggerDetailsHTMLURL string            `json:"trigger_details_html_url,omitempty"`
	TriggerType           string            `json:"trigger_type,omitempty"`
	LastStatusChangeOn    string            `json:"last_status_change_on,omitempty"`
	LastStatusChangeBy    *User             `json:"last_status_change_by,omitempty"`
	NumberOfEscalations   int               `json:"number_of_escalations,omitempty"`
	ResolvedByUser        *User             `json:"resolved_by_user,omitempty"`
	AssignedToUser        *User             `json:"assigned_to_user,omitempty"`
	AssignedTo            []*User           `json:"assigned_to,omitempty"`
}

// Incidents is a list of incidents
type Incidents struct {
	Pagination
	Incidents []Incident
}

// IncidentSummary type
type IncidentSummary struct {
	Subject     string //`json:"subject,omitempty"`
	Description string //`json:"description,omitempty"`
}

// Get returns a single incident by id if found
func (s *IncidentsService) Get(id string) (*Incident, *http.Response, error) {
	incident := new(Incident)

	res, err := s.client.Get("incidents/"+id, incident)
	if err != nil {
		return nil, res, err
	}

	return incident, res, nil
}

// IncidentsOptions provides optional parameters to list requests
type IncidentsOptions struct {
	Status string `url:"status,omitempty"`
	SortBy string `url:"sort_by,omitempty"`
	Since  string `url:"since,omitempty"`
	Until  string `url:"until,omitempty"`
}

// List returns a list of incidents
func (s *IncidentsService) List(opt *IncidentsOptions) (Incidents, *http.Response, error) {
	var incidents Incidents
	u, err := addOptions("incidents", opt)
	if err != nil {
		return incidents, nil, err
	}

	res, err := s.client.Get(u, incidents)
	if err != nil {
		return incidents, res, err
	}

	return incidents, res, err
}
<|endoftext|>


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

package etcdserver

import (
	"fmt"
	"net/http"
	"path"
	"sort"
	"time"

	"github.com/coreos/etcd/pkg/netutil"
	"github.com/coreos/etcd/pkg/types"
)

// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
type ServerConfig struct {
	Name           string
	DiscoveryURL   string
	DiscoveryProxy string
	ClientURLs     types.URLs
	PeerURLs       types.URLs
	DataDir        string
	// DedicatedWALDir config will make the etcd to write the WAL to the WALDir
	// rather than the dataDir/member/wal.
	DedicatedWALDir     string
	SnapCount           uint64
	MaxSnapFiles        uint
	MaxWALFiles         uint
	InitialPeerURLsMap  types.URLsMap
	InitialClusterToken string
	NewCluster          bool
	ForceNewCluster     bool
	Transport           *http.Transport

	TickMs        uint
	ElectionTicks int

	V3demo bool

	StrictReconfigCheck bool
}

// VerifyBootstrapConfig sanity-checks the initial config for bootstrap case
// and returns an error for things that should never happen.
func (c *ServerConfig) VerifyBootstrap() error {
	if err := c.verifyLocalMember(true); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
		return fmt.Errorf("initial cluster unset and no discovery URL found")
	}
	return nil
}

// VerifyJoinExisting sanity-checks the initial config for join existing cluster
// case and returns an error for things that should never happen.
func (c *ServerConfig) VerifyJoinExisting() error {
	// no need for strict checking since the member have announced its
	// peer urls to the cluster before starting and do not have to set
	// it in the configuration again.
	if err := c.verifyLocalMember(false); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.DiscoveryURL != "" {
		return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
	}
	return nil
}

// verifyLocalMember verifies the configured member is in configured
// cluster. If strict is set, it also verifies the configured member
// has the same peer urls as configured advertised peer urls.
func (c *ServerConfig) verifyLocalMember(strict bool) error {
	urls := c.InitialPeerURLsMap[c.Name]
	// Make sure the cluster at least contains the local server.
	if urls == nil {
		return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
	}

	// Advertised peer URLs must match those in the cluster peer list
	apurls := c.PeerURLs.StringSlice()
	sort.Strings(apurls)
	urls.Sort()
	if strict {
		if !netutil.URLStringsEqual(apurls, urls.StringSlice()) {
			return fmt.Errorf("--initial-cluster must include %s=%s given --initial-advertise-peer-urls=%s", c.Name, apurls, apurls)
		}
	}
	return nil
}

func (c *ServerConfig) MemberDir() string { return path.Join(c.DataDir, "member") }

func (c *ServerConfig) WALDir() string {
	if c.DedicatedWALDir != "" {
		return c.DedicatedWALDir
	}
	return path.Join(c.MemberDir(), "wal")
}

func (c *ServerConfig) SnapDir() string { return path.Join(c.MemberDir(), "snap") }

func (c *ServerConfig) StorageDir() string { return path.Join(c.MemberDir(), "storage") }

func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }

// ReqTimeout returns timeout for request to finish.
func (c *ServerConfig) ReqTimeout() time.Duration {
	// 5s for queue waiting, computation and disk IO delay
	// + 2 * election timeout for possible leader election
	return 5*time.Second + 2*time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond
}

func (c *ServerConfig) PrintWithInitial() { c.print(true) }

func (c *ServerConfig) Print() { c.print(false) }

func (c *ServerConfig) print(initial bool) {
	plog.Infof("name = %s", c.Name)
	if c.ForceNewCluster {
		plog.Infof("force new cluster")
	}
	plog.Infof("data dir = %s", c.DataDir)
	plog.Infof("member dir = %s", c.MemberDir())
	if c.DedicatedWALDir != "" {
		plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir)
	}
	plog.Infof("heartbeat = %dms", c.TickMs)
	plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs))
	plog.Infof("snapshot count = %d", c.SnapCount)
	if len(c.DiscoveryURL) != 0 {
		plog.Infof("discovery URL= %s", c.DiscoveryURL)
		if len(c.DiscoveryProxy) != 0 {
			plog.Infof("discovery proxy = %s", c.DiscoveryProxy)
		}
	}
	plog.Infof("advertise client URLs = %s", c.ClientURLs)
	if initial {
		plog.Infof("initial advertise peer URLs = %s", c.PeerURLs)
		plog.Infof("initial cluster = %s", c.InitialPeerURLsMap)
	}
}

func checkDuplicateURL(urlsmap types.URLsMap) bool {
	um := make(map[string]bool)
	for _, urls := range urlsmap {
		for _, url := range urls {
			u := url.String()
			if um[u] {
				return true
			}
			um[u] = true
		}
	}
	return false
}
<commit_msg>etcdsever: mismatch error uses the same format as the corresponding flags<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package etcdserver

import (
	"fmt"
	"net/http"
	"path"
	"sort"
	"strings"
	"time"

	"github.com/coreos/etcd/pkg/netutil"
	"github.com/coreos/etcd/pkg/types"
)

// ServerConfig holds the configuration of etcd as taken from the command line or discovery.
type ServerConfig struct {
	Name           string
	DiscoveryURL   string
	DiscoveryProxy string
	ClientURLs     types.URLs
	PeerURLs       types.URLs
	DataDir        string
	// DedicatedWALDir config will make the etcd to write the WAL to the WALDir
	// rather than the dataDir/member/wal.
	DedicatedWALDir     string
	SnapCount           uint64
	MaxSnapFiles        uint
	MaxWALFiles         uint
	InitialPeerURLsMap  types.URLsMap
	InitialClusterToken string
	NewCluster          bool
	ForceNewCluster     bool
	Transport           *http.Transport

	TickMs        uint
	ElectionTicks int

	V3demo bool

	StrictReconfigCheck bool
}

// VerifyBootstrapConfig sanity-checks the initial config for bootstrap case
// and returns an error for things that should never happen.
func (c *ServerConfig) VerifyBootstrap() error {
	if err := c.verifyLocalMember(true); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.InitialPeerURLsMap.String() == "" && c.DiscoveryURL == "" {
		return fmt.Errorf("initial cluster unset and no discovery URL found")
	}
	return nil
}

// VerifyJoinExisting sanity-checks the initial config for join existing cluster
// case and returns an error for things that should never happen.
func (c *ServerConfig) VerifyJoinExisting() error {
	// no need for strict checking since the member have announced its
	// peer urls to the cluster before starting and do not have to set
	// it in the configuration again.
	if err := c.verifyLocalMember(false); err != nil {
		return err
	}
	if checkDuplicateURL(c.InitialPeerURLsMap) {
		return fmt.Errorf("initial cluster %s has duplicate url", c.InitialPeerURLsMap)
	}
	if c.DiscoveryURL != "" {
		return fmt.Errorf("discovery URL should not be set when joining existing initial cluster")
	}
	return nil
}

// verifyLocalMember verifies the configured member is in configured
// cluster. If strict is set, it also verifies the configured member
// has the same peer urls as configured advertised peer urls.
func (c *ServerConfig) verifyLocalMember(strict bool) error {
	urls := c.InitialPeerURLsMap[c.Name]
	// Make sure the cluster at least contains the local server.
	if urls == nil {
		return fmt.Errorf("couldn't find local name %q in the initial cluster configuration", c.Name)
	}

	// Advertised peer URLs must match those in the cluster peer list
	apurls := c.PeerURLs.StringSlice()
	sort.Strings(apurls)
	urls.Sort()
	if strict {
		if !netutil.URLStringsEqual(apurls, urls.StringSlice()) {
			umap := map[string]types.URLs{c.Name: c.PeerURLs}
			return fmt.Errorf("--initial-cluster must include %s given --initial-advertise-peer-urls=%s", types.URLsMap(umap).String(), strings.Join(apurls, ","))
		}
	}
	return nil
}

func (c *ServerConfig) MemberDir() string { return path.Join(c.DataDir, "member") }

func (c *ServerConfig) WALDir() string {
	if c.DedicatedWALDir != "" {
		return c.DedicatedWALDir
	}
	return path.Join(c.MemberDir(), "wal")
}

func (c *ServerConfig) SnapDir() string { return path.Join(c.MemberDir(), "snap") }

func (c *ServerConfig) StorageDir() string { return path.Join(c.MemberDir(), "storage") }

func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }

// ReqTimeout returns timeout for request to finish.
func (c *ServerConfig) ReqTimeout() time.Duration {
	// 5s for queue waiting, computation and disk IO delay
	// + 2 * election timeout for possible leader election
	return 5*time.Second + 2*time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond
}

func (c *ServerConfig) PrintWithInitial() { c.print(true) }

func (c *ServerConfig) Print() { c.print(false) }

func (c *ServerConfig) print(initial bool) {
	plog.Infof("name = %s", c.Name)
	if c.ForceNewCluster {
		plog.Infof("force new cluster")
	}
	plog.Infof("data dir = %s", c.DataDir)
	plog.Infof("member dir = %s", c.MemberDir())
	if c.DedicatedWALDir != "" {
		plog.Infof("dedicated WAL dir = %s", c.DedicatedWALDir)
	}
	plog.Infof("heartbeat = %dms", c.TickMs)
	plog.Infof("election = %dms", c.ElectionTicks*int(c.TickMs))
	plog.Infof("snapshot count = %d", c.SnapCount)
	if len(c.DiscoveryURL) != 0 {
		plog.Infof("discovery URL= %s", c.DiscoveryURL)
		if len(c.DiscoveryProxy) != 0 {
			plog.Infof("discovery proxy = %s", c.DiscoveryProxy)
		}
	}
	plog.Infof("advertise client URLs = %s", c.ClientURLs)
	if initial {
		plog.Infof("initial advertise peer URLs = %s", c.PeerURLs)
		plog.Infof("initial cluster = %s", c.InitialPeerURLsMap)
	}
}

func checkDuplicateURL(urlsmap types.URLsMap) bool {
	um := make(map[string]bool)
	for _, urls := range urlsmap {
		for _, url := range urls {
			u := url.String()
			if um[u] {
				return true
			}
			um[u] = true
		}
	}
	return false
}
<|endoftext|>


<commit_before>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// 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 event

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

func TestCocurrent(t *testing.T) {
	InitRounter()
	var done sync.WaitGroup
	quit := make(chan struct{})
	rwtest := func(station Station) {
		defer done.Done()
		channel := make(chan *Event)
		if station != nil {
			StationRegister(station)
			defer StationUnregister(station)
		}
		sub := Subscribe(station, channel, RouterTestString, "")
		done.Add(1)
		go func() {
			defer done.Done()
			defer sub.Unsubscribe()
			for {
				select {
				case <-quit:
					return
				case <-channel:
				}
			}
		}()
		for {
			select {
			case <-quit:
				return
			default:
				SendTo(nil, station, RouterTestString, "Cocurrent")
			}
		}
	}
	done.Add(1)
	go rwtest(nil)
	for i := 0; i < 10; i++ {
		done.Add(1)
		go rwtest(NewLocalStation(fmt.Sprint("TestStation", i), nil))
	}
	nTime := int64(0)
	totalTime := int64(0)
	maxTime := time.Nanosecond
	minTime := time.Hour
	var testTime = time.Now()
	for {
		nTime++
		station1 := NewLocalStation("TestStation1", nil)
		var channel1 chan *Event
		var start = time.Now()
		sub := Subscribe(station1, channel1, RouterTestString, "")
		duration := time.Now().Sub(start)
		StationUnregister(station1)
		sub.Unsubscribe()
		if duration > maxTime {
			maxTime = duration
		}
		if duration < minTime {
			minTime = duration
		}
		totalTime += duration.Nanoseconds()
		if duration > 100*1000*1000 { // 10ms
			t.Fatalf("time is too long!")
			break
		}
		if time.Now().Sub(testTime) > 15*time.Second {
			break
		}
	}
	close(quit)
	done.Wait()
	// go test -v
	t.Logf("total %d %d avg %d max %d min %d ns\n", nTime, totalTime, totalTime/nTime, maxTime.Nanoseconds(), minTime.Nanoseconds())
}

func TestSendEventToStation(t *testing.T) {
	InitRounter()
	type testStation struct {
		station Station
		channel chan *Event
	}
	var (
		done     sync.WaitGroup
		station1 = &testStation{NewLocalStation("TestStation1", nil), make(chan *Event)}
		station2 = &testStation{NewLocalStation("TestStation2", nil), make(chan *Event)}
		station3 = &testStation{NewLocalStation("TestStation3", nil), make(chan *Event)}
		sub1     Subscription
		sub2     Subscription
		sub3     Subscription
	)

	sub1 = Subscribe(station1.station, station1.channel, RouterTestString, "")
	sub2 = Subscribe(station2.station, station2.channel, RouterTestString, "")
	sub3 = Subscribe(station3.station, station3.channel, RouterTestString, "")

	errorList := []string{}
	recvAndCheck := func(station *testStation, expect interface{}) {
		timer := time.After(time.Second)
		select {
		case e := <-station.channel:
			if rstr := e.Data.(string); rstr != expect.(string) {
				errorList = append(errorList, fmt.Sprintf("wrong string '%s', want '%s'", rstr, expect.(string)))
			}
		case <-timer:
			if expect != nil {
				errorList = append(errorList, fmt.Sprintf("timeout! want '%s'", expect.(string)))
			}
		}
		done.Done()
	}

	done.Add(3)
	msg := "Hello Fractal!"
	go recvAndCheck(station1, nil)
	go recvAndCheck(station2, nil)
	go recvAndCheck(station3, nil)
	go SendTo(nil, nil, RouterTestString, msg)
	done.Wait()

	done.Add(3)
	go recvAndCheck(station1, "1")
	go recvAndCheck(station2, "2")
	go recvAndCheck(station3, "3")
	go SendTo(nil, GetStationByName("TestStation1"), RouterTestString, "1")
	go SendTo(nil, GetStationByName("TestStation2"), RouterTestString, "2")
	go SendTo(nil, GetStationByName("TestStation3"), RouterTestString, "3")
	done.Wait()
	sub1.Unsubscribe()
	sub2.Unsubscribe()
	sub3.Unsubscribe()
	errStr := "\n"
	for _, e := range errorList {
		errStr += fmt.Sprintln(e)
	}
	if errStr != "\n" {
		t.Fatal(errStr)
	}
}

func TestSendEvent(t *testing.T) {
	InitRounter()
	var (
		done    sync.WaitGroup
		nsubs   = 10
		mutex   sync.RWMutex
		receive = 0
	)
	subscriber := func(ch chan *Event) {
		<-ch
		mutex.Lock()
		receive++
		mutex.Unlock()
		done.Done()
	}
	done.Add(nsubs)

	num := int(1)
	for i := 0; i < nsubs; i++ {
		ch := make(chan *Event)
		Subscribe(nil, ch, RouterTestInt, num)
		go subscriber(ch)
	}

	event := &Event{
		Typecode: RouterTestInt,
		Data:     num,
	}
	nsend := SendEvent(event)

	done.Wait()

	if nsend != nsubs {
		t.Fatalf("wrong int %d, want %d", nsubs, nsend)
	}

	if receive != nsubs {
		t.Fatalf("wrong int %d, want %d", nsubs, receive)
	}

	typ := GetTypeByCode(RouterTestInt)
	if reflect.TypeOf(event.Data) != typ {
		t.Fatalf("wrong type")
	}
}

func TestUnsubscribe(t *testing.T) {
	InitRounter()
	var (
		done  sync.WaitGroup
		nsubs = 1000
	)

	subscriber := func(sub Subscription) {
		sub.Unsubscribe()
		done.Done()
	}

	done.Add(nsubs)
	for i := 0; i < nsubs; i++ {
		ch := make(chan *Event)
		sub := Subscribe(nil, ch, RouterTestInt64, reflect.Int64)
		go subscriber(sub)
	}

	event := &Event{
		Typecode: RouterTestInt64,
		Data:     int64(1),
	}
	SendEvent(event)
	done.Wait()
}
<commit_msg>add more error infomation<commit_after>// Copyright 2018 The Fractal Team Authors
// This file is part of the fractal project.
//
// 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 event

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

func TestCocurrent(t *testing.T) {
	InitRounter()
	var done sync.WaitGroup
	quit := make(chan struct{})
	rwtest := func(station Station) {
		defer done.Done()
		channel := make(chan *Event)
		if station != nil {
			StationRegister(station)
			defer StationUnregister(station)
		}
		sub := Subscribe(station, channel, RouterTestString, "")
		done.Add(1)
		go func() {
			defer done.Done()
			defer sub.Unsubscribe()
			for {
				select {
				case <-quit:
					return
				case <-channel:
				}
			}
		}()
		for {
			select {
			case <-quit:
				return
			default:
				SendTo(nil, station, RouterTestString, "Cocurrent")
			}
		}
	}
	done.Add(1)
	go rwtest(nil)
	for i := 0; i < 10; i++ {
		done.Add(1)
		go rwtest(NewLocalStation(fmt.Sprint("TestStation", i), nil))
	}
	nTime := int64(0)
	totalTime := int64(0)
	maxTime := time.Nanosecond
	minTime := time.Hour
	var testTime = time.Now()
	for {
		nTime++
		station1 := NewLocalStation("TestStation1", nil)
		var channel1 chan *Event
		var start = time.Now()
		sub := Subscribe(station1, channel1, RouterTestString, "")
		duration := time.Now().Sub(start)
		StationUnregister(station1)
		sub.Unsubscribe()
		if duration > maxTime {
			maxTime = duration
		}
		if duration < minTime {
			minTime = duration
		}
		totalTime += duration.Nanoseconds()
		if duration > 10*1000*1000 { // 10ms
			t.Fatalf("time=%d(ns) is too long!", duration)
			break
		}
		if time.Now().Sub(testTime) > 15*time.Second {
			break
		}
	}
	close(quit)
	done.Wait()
	// go test -v
	t.Logf("total %d %d avg %d max %d min %d ns\n", nTime, totalTime, totalTime/nTime, maxTime.Nanoseconds(), minTime.Nanoseconds())
}

func TestSendEventToStation(t *testing.T) {
	InitRounter()
	type testStation struct {
		station Station
		channel chan *Event
	}
	var (
		done     sync.WaitGroup
		station1 = &testStation{NewLocalStation("TestStation1", nil), make(chan *Event)}
		station2 = &testStation{NewLocalStation("TestStation2", nil), make(chan *Event)}
		station3 = &testStation{NewLocalStation("TestStation3", nil), make(chan *Event)}
		sub1     Subscription
		sub2     Subscription
		sub3     Subscription
	)

	sub1 = Subscribe(station1.station, station1.channel, RouterTestString, "")
	sub2 = Subscribe(station2.station, station2.channel, RouterTestString, "")
	sub3 = Subscribe(station3.station, station3.channel, RouterTestString, "")

	errorList := []string{}
	recvAndCheck := func(station *testStation, expect interface{}) {
		timer := time.After(time.Second)
		select {
		case e := <-station.channel:
			if rstr := e.Data.(string); rstr != expect.(string) {
				errorList = append(errorList, fmt.Sprintf("wrong string '%s', want '%s'", rstr, expect.(string)))
			}
		case <-timer:
			if expect != nil {
				errorList = append(errorList, fmt.Sprintf("timeout! want '%s'", expect.(string)))
			}
		}
		done.Done()
	}

	done.Add(3)
	msg := "Hello Fractal!"
	go recvAndCheck(station1, nil)
	go recvAndCheck(station2, nil)
	go recvAndCheck(station3, nil)
	go SendTo(nil, nil, RouterTestString, msg)
	done.Wait()

	done.Add(3)
	go recvAndCheck(station1, "1")
	go recvAndCheck(station2, "2")
	go recvAndCheck(station3, "3")
	go SendTo(nil, GetStationByName("TestStation1"), RouterTestString, "1")
	go SendTo(nil, GetStationByName("TestStation2"), RouterTestString, "2")
	go SendTo(nil, GetStationByName("TestStation3"), RouterTestString, "3")
	done.Wait()
	sub1.Unsubscribe()
	sub2.Unsubscribe()
	sub3.Unsubscribe()
	errStr := "\n"
	for _, e := range errorList {
		errStr += fmt.Sprintln(e)
	}
	if errStr != "\n" {
		t.Fatal(errStr)
	}
}

func TestSendEvent(t *testing.T) {
	InitRounter()
	var (
		done    sync.WaitGroup
		nsubs   = 10
		mutex   sync.RWMutex
		receive = 0
	)
	subscriber := func(ch chan *Event) {
		<-ch
		mutex.Lock()
		receive++
		mutex.Unlock()
		done.Done()
	}
	done.Add(nsubs)

	num := int(1)
	for i := 0; i < nsubs; i++ {
		ch := make(chan *Event)
		Subscribe(nil, ch, RouterTestInt, num)
		go subscriber(ch)
	}

	event := &Event{
		Typecode: RouterTestInt,
		Data:     num,
	}
	nsend := SendEvent(event)

	done.Wait()

	if nsend != nsubs {
		t.Fatalf("wrong int %d, want %d", nsubs, nsend)
	}

	if receive != nsubs {
		t.Fatalf("wrong int %d, want %d", nsubs, receive)
	}

	typ := GetTypeByCode(RouterTestInt)
	if reflect.TypeOf(event.Data) != typ {
		t.Fatalf("wrong type")
	}
}

func TestUnsubscribe(t *testing.T) {
	InitRounter()
	var (
		done  sync.WaitGroup
		nsubs = 1000
	)

	subscriber := func(sub Subscription) {
		sub.Unsubscribe()
		done.Done()
	}

	done.Add(nsubs)
	for i := 0; i < nsubs; i++ {
		ch := make(chan *Event)
		sub := Subscribe(nil, ch, RouterTestInt64, reflect.Int64)
		go subscriber(sub)
	}

	event := &Event{
		Typecode: RouterTestInt64,
		Data:     int64(1),
	}
	SendEvent(event)
	done.Wait()
}
<|endoftext|>


<commit_before>package routing

import (
	"fmt"
	"time"

	. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"

	. "code.cloudfoundry.org/cf-routing-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/skip_messages"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = RoutingDescribe("Multiple App Ports", func() {
	var (
		app             string
		secondRoute     string
		latticeAppAsset = assets.NewAssets().LatticeApp
		SleepTimeOut    = 45 * time.Second
	)

	BeforeEach(func() {
		if Config.Backend != "diego" {
			Skip(skip_messages.SkipDiegoMessage)
		}
		app = random_name.CATSRandomName("APP")
		cmd := fmt.Sprintf("lattice-app --ports=7777,8888,8080")

		PushAppNoStart(app, latticeAppAsset, Config.GoBuildpackName, Config.AppsDomain, Config.CfPushTimeoutDuration(), DEFAULT_MEMORY_LIMIT, "-c", cmd)
		EnableDiego(app, Config.DefaultTimeoutDuration())
		StartApp(app, APP_START_TIMEOUT)
		if Config.SleepTimeoutDuration() > 0 {
			SleepTimeOut = time.Duration(Config.SleepTimeout) * time.Second
		}
	})

	AfterEach(func() {
		AppReport(app, Config.DefaultTimeoutDuration())
		DeleteApp(app, Config.DefaultTimeoutDuration())
	})

	Context("when app only has single route", func() {
		Context("when no ports are specified for the app", func() {
			It("should listen on the default app port", func() {
				Eventually(func() string {
					return helpers.CurlApp(Config, app, "/port")
				}, Config.DefaultTimeoutDuration(), "5s").Should(ContainSubstring("8080"))
			})
		})
	})

	Context("when app has multiple ports mapped", func() {
		BeforeEach(func() {
			UpdatePorts(app, []uint16{7777, 8888, 8080}, Config.DefaultTimeoutDuration())
			// create 2nd route
			spacename := TestSetup.RegularUserContext().Space
			secondRoute = fmt.Sprintf("%s-two", app)
			CreateRoute(secondRoute, "", spacename, Config.AppsDomain, Config.DefaultTimeoutDuration())

			// map app route to other port
			CreateRouteMapping(app, secondRoute, 0, 7777, Config.DefaultTimeoutDuration())
		})

		It("should listen on multiple ports", func() {
			Eventually(func() string {
				return helpers.CurlApp(Config, app, "/")
			}, Config.DefaultTimeoutDuration(), "5s").Should(ContainSubstring("Lattice"))

			Consistently(func() string {
				return helpers.CurlApp(Config, app, "/port")
			}, SleepTimeout, "5s").Should(ContainSubstring("8080"))

			Eventually(func() string {
				return helpers.CurlApp(Config, secondRoute, "/port")
			}, Config.DefaultTimeoutDuration(), "5s").Should(ContainSubstring("7777"))
		})
	})
})
<commit_msg>Fix typo for timeout<commit_after>package routing

import (
	"fmt"
	"time"

	. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"

	. "code.cloudfoundry.org/cf-routing-test-helpers/helpers"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/assets"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name"
	"github.com/cloudfoundry/cf-acceptance-tests/helpers/skip_messages"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = RoutingDescribe("Multiple App Ports", func() {
	var (
		app             string
		secondRoute     string
		latticeAppAsset = assets.NewAssets().LatticeApp
		SleepTimeOut    = 45 * time.Second
	)

	BeforeEach(func() {
		if Config.Backend != "diego" {
			Skip(skip_messages.SkipDiegoMessage)
		}
		app = random_name.CATSRandomName("APP")
		cmd := fmt.Sprintf("lattice-app --ports=7777,8888,8080")

		PushAppNoStart(app, latticeAppAsset, Config.GoBuildpackName, Config.AppsDomain, Config.CfPushTimeoutDuration(), DEFAULT_MEMORY_LIMIT, "-c", cmd)
		EnableDiego(app, Config.DefaultTimeoutDuration())
		StartApp(app, APP_START_TIMEOUT)
		if Config.SleepTimeoutDuration() > 0 {
			SleepTimeOut = time.Duration(Config.SleepTimeout) * time.Second
		}
	})

	AfterEach(func() {
		AppReport(app, Config.DefaultTimeoutDuration())
		DeleteApp(app, Config.DefaultTimeoutDuration())
	})

	Context("when app only has single route", func() {
		Context("when no ports are specified for the app", func() {
			It("should listen on the default app port", func() {
				Eventually(func() string {
					return helpers.CurlApp(Config, app, "/port")
				}, Config.DefaultTimeoutDuration(), "5s").Should(ContainSubstring("8080"))
			})
		})
	})

	Context("when app has multiple ports mapped", func() {
		BeforeEach(func() {
			UpdatePorts(app, []uint16{7777, 8888, 8080}, Config.DefaultTimeoutDuration())
			// create 2nd route
			spacename := TestSetup.RegularUserContext().Space
			secondRoute = fmt.Sprintf("%s-two", app)
			CreateRoute(secondRoute, "", spacename, Config.AppsDomain, Config.DefaultTimeoutDuration())

			// map app route to other port
			CreateRouteMapping(app, secondRoute, 0, 7777, Config.DefaultTimeoutDuration())
		})

		It("should listen on multiple ports", func() {
			Eventually(func() string {
				return helpers.CurlApp(Config, app, "/")
			}, Config.DefaultTimeoutDuration(), "5s").Should(ContainSubstring("Lattice"))

			Consistently(func() string {
				return helpers.CurlApp(Config, app, "/port")
			}, SleepTimeOut, "5s").Should(ContainSubstring("8080"))

			Eventually(func() string {
				return helpers.CurlApp(Config, secondRoute, "/port")
			}, Config.DefaultTimeoutDuration(), "5s").Should(ContainSubstring("7777"))
		})
	})
})
<|endoftext|>


<commit_before>package zego

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type Resource struct {
	//Headers     http.Header
	Response interface{}
	Raw      string
}

type Auth struct {
	Username  string
	Password  string
	Subdomain string
}

func errHandler(err error) {
	if err != nil {
		log.Print(err)
	}
}

func api(auth Auth, meth string, path string, params string) (*Resource, error) {

	trn := &http.Transport{}

	client := &http.Client{
		Transport: trn,
	}

	var URL string
	if strings.HasPrefix(auth.Subdomain, "http") {

		// path contains entire url
		if strings.HasPrefix(path, "http") {
			URL = path
		} else {
			URL = auth.Subdomain + "/api/v2/" + path
		}
	} else {
		URL = "https://" + auth.Subdomain + "/api/v2/" + path
	}

	req, err := http.NewRequest(meth, URL, bytes.NewBufferString(params))
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", "application/json")

	req.SetBasicAuth(auth.Username, auth.Password)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return &Resource{Response: &resp, Raw: string(data)}, nil

}
<commit_msg>fixed url building logic<commit_after>package zego

import (
	"bytes"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type Resource struct {
	//Headers     http.Header
	Response interface{}
	Raw      string
}

type Auth struct {
	Username  string
	Password  string
	Subdomain string
}

func errHandler(err error) {
	if err != nil {
		log.Print(err)
	}
}

func api(auth Auth, meth string, path string, params string) (*Resource, error) {

	trn := &http.Transport{}

	client := &http.Client{
		Transport: trn,
	}

	var URL string

	// Check if entire URL is in path
	if strings.HasPrefix(path, "http") {
		URL = path

		// Otherwise build url from auth components
	} else {
		if strings.HasPrefix(auth.Subdomain, "http") {
			URL = auth.Subdomain + "/api/v2/" + path
		} else {
			URL = "https://" + auth.Subdomain + "/api/v2/" + path
		}
	}

	req, err := http.NewRequest(meth, URL, bytes.NewBufferString(params))
	if err != nil {
		return nil, err
	}

	req.Header.Add("Content-Type", "application/json")

	req.SetBasicAuth(auth.Username, auth.Password)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	data, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return &Resource{Response: &resp, Raw: string(data)}, nil

}
<|endoftext|>


<commit_before>package ledis

import (
	"errors"
	"github.com/siddontang/ledisdb/store"
	"sync"
)

var (
	ErrNestTx = errors.New("nest transaction not supported")
	ErrTxDone = errors.New("Transaction has already been committed or rolled back")
)

type batch struct {
	l *Ledis

	store.WriteBatch

	sync.Locker

	logs [][]byte

	tx *Tx
}

type dbBatchLocker struct {
	sync.Mutex
	dbLock *sync.RWMutex
}

type txBatchLocker struct {
}

func (l *txBatchLocker) Lock() {
}

func (l *txBatchLocker) Unlock() {
}

func (l *dbBatchLocker) Lock() {
	l.dbLock.RLock()
	l.Mutex.Lock()
}

func (l *dbBatchLocker) Unlock() {
	l.Mutex.Unlock()
	l.dbLock.RUnlock()
}

func (db *DB) newBatch() *batch {
	b := new(batch)

	b.WriteBatch = db.bucket.NewWriteBatch()
	b.Locker = &dbBatchLocker{dbLock: db.dbLock}
	b.l = db.l

	return b
}

func (b *batch) Commit() error {
	b.l.Lock()
	defer b.l.Unlock()

	err := b.WriteBatch.Commit()

	if b.l.binlog != nil {
		if err == nil {
			if b.tx == nil {
				b.l.binlog.Log(b.logs...)
			} else {
				b.tx.logs = append(b.tx.logs, b.logs...)
			}
		}
		b.logs = [][]byte{}
	}

	return err
}

func (b *batch) Lock() {
	b.Locker.Lock()
}

func (b *batch) Unlock() {
	if b.l.binlog != nil {
		b.logs = [][]byte{}
	}
	b.Rollback()
	b.Locker.Unlock()
}

func (b *batch) Put(key []byte, value []byte) {
	if b.l.binlog != nil {
		buf := encodeBinLogPut(key, value)
		b.logs = append(b.logs, buf)
	}
	b.WriteBatch.Put(key, value)
}

func (b *batch) Delete(key []byte) {
	if b.l.binlog != nil {
		buf := encodeBinLogDelete(key)
		b.logs = append(b.logs, buf)
	}
	b.WriteBatch.Delete(key)
}

type Tx struct {
	*DB

	tx *store.Tx

	logs [][]byte
}

func (db *DB) IsTransaction() bool {
	return db.isTx
}

// Begin a transaction, it will block all other write operations before calling Commit or Rollback.
// You must be very careful to prevent long-time transaction.
func (db *DB) Begin() (*Tx, error) {
	if db.isTx {
		return nil, ErrNestTx
	}

	tx := new(Tx)

	tx.DB = new(DB)
	tx.DB.dbLock = db.dbLock

	tx.DB.dbLock.Lock()

	tx.DB.l = db.l

	tx.DB.sdb = db.sdb

	var err error
	tx.tx, err = db.sdb.Begin()
	if err != nil {
		tx.DB.dbLock.Unlock()
		return nil, err
	}

	tx.DB.bucket = tx.tx

	tx.DB.isTx = true

	tx.DB.index = db.index

	tx.DB.kvBatch = tx.newBatch()
	tx.DB.listBatch = tx.newBatch()
	tx.DB.hashBatch = tx.newBatch()
	tx.DB.zsetBatch = tx.newBatch()
	tx.DB.binBatch = tx.newBatch()
	tx.DB.setBatch = tx.newBatch()

	return tx, nil
}

func (tx *Tx) Commit() error {
	if tx.tx == nil {
		return ErrTxDone
	}

	tx.l.Lock()
	err := tx.tx.Commit()
	tx.tx = nil

	if len(tx.logs) > 0 {
		tx.l.binlog.Log(tx.logs...)
	}

	tx.l.Unlock()

	tx.DB.dbLock.Unlock()
	tx.DB = nil
	return err
}

func (tx *Tx) Rollback() error {
	if tx.tx == nil {
		return ErrTxDone
	}

	err := tx.tx.Rollback()
	tx.tx = nil

	tx.DB.dbLock.Unlock()
	tx.DB = nil
	return err
}

func (tx *Tx) newBatch() *batch {
	b := new(batch)

	b.l = tx.l
	b.WriteBatch = tx.tx.NewWriteBatch()
	b.Locker = &txBatchLocker{}
	b.tx = tx

	return b
}
<commit_msg>tx commit bugfix<commit_after>package ledis

import (
	"errors"
	"github.com/siddontang/ledisdb/store"
	"sync"
)

var (
	ErrNestTx = errors.New("nest transaction not supported")
	ErrTxDone = errors.New("Transaction has already been committed or rolled back")
)

type batch struct {
	l *Ledis

	store.WriteBatch

	sync.Locker

	logs [][]byte

	tx *Tx
}

type dbBatchLocker struct {
	sync.Mutex
	dbLock *sync.RWMutex
}

type txBatchLocker struct {
}

func (l *txBatchLocker) Lock() {
}

func (l *txBatchLocker) Unlock() {
}

func (l *dbBatchLocker) Lock() {
	l.dbLock.RLock()
	l.Mutex.Lock()
}

func (l *dbBatchLocker) Unlock() {
	l.Mutex.Unlock()
	l.dbLock.RUnlock()
}

func (db *DB) newBatch() *batch {
	b := new(batch)

	b.WriteBatch = db.bucket.NewWriteBatch()
	b.Locker = &dbBatchLocker{dbLock: db.dbLock}
	b.l = db.l

	return b
}

func (b *batch) Commit() error {
	b.l.Lock()
	defer b.l.Unlock()

	err := b.WriteBatch.Commit()

	if b.l.binlog != nil {
		if err == nil {
			if b.tx == nil {
				b.l.binlog.Log(b.logs...)
			} else {
				b.tx.logs = append(b.tx.logs, b.logs...)
			}
		}
		b.logs = [][]byte{}
	}

	return err
}

func (b *batch) Lock() {
	b.Locker.Lock()
}

func (b *batch) Unlock() {
	if b.l.binlog != nil {
		b.logs = [][]byte{}
	}
	b.Rollback()
	b.Locker.Unlock()
}

func (b *batch) Put(key []byte, value []byte) {
	if b.l.binlog != nil {
		buf := encodeBinLogPut(key, value)
		b.logs = append(b.logs, buf)
	}
	b.WriteBatch.Put(key, value)
}

func (b *batch) Delete(key []byte) {
	if b.l.binlog != nil {
		buf := encodeBinLogDelete(key)
		b.logs = append(b.logs, buf)
	}
	b.WriteBatch.Delete(key)
}

type Tx struct {
	*DB

	tx *store.Tx

	logs [][]byte

	index uint8
}

func (db *DB) IsTransaction() bool {
	return db.isTx
}

// Begin a transaction, it will block all other write operations before calling Commit or Rollback.
// You must be very careful to prevent long-time transaction.
func (db *DB) Begin() (*Tx, error) {
	if db.isTx {
		return nil, ErrNestTx
	}

	tx := new(Tx)

	tx.DB = new(DB)
	tx.DB.dbLock = db.dbLock

	tx.DB.dbLock.Lock()

	tx.DB.l = db.l
	tx.index = db.index

	tx.DB.sdb = db.sdb

	var err error
	tx.tx, err = db.sdb.Begin()
	if err != nil {
		tx.DB.dbLock.Unlock()
		return nil, err
	}

	tx.DB.bucket = tx.tx

	tx.DB.isTx = true

	tx.DB.index = db.index

	tx.DB.kvBatch = tx.newBatch()
	tx.DB.listBatch = tx.newBatch()
	tx.DB.hashBatch = tx.newBatch()
	tx.DB.zsetBatch = tx.newBatch()
	tx.DB.binBatch = tx.newBatch()
	tx.DB.setBatch = tx.newBatch()

	return tx, nil
}

func (tx *Tx) Commit() error {
	if tx.tx == nil {
		return ErrTxDone
	}

	tx.l.Lock()
	err := tx.tx.Commit()
	tx.tx = nil

	if len(tx.logs) > 0 {
		tx.l.binlog.Log(tx.logs...)
	}

	tx.l.Unlock()

	tx.DB.dbLock.Unlock()
	tx.DB = nil
	return err
}

func (tx *Tx) Rollback() error {
	if tx.tx == nil {
		return ErrTxDone
	}

	err := tx.tx.Rollback()
	tx.tx = nil

	tx.DB.dbLock.Unlock()
	tx.DB = nil
	return err
}

func (tx *Tx) newBatch() *batch {
	b := new(batch)

	b.l = tx.l
	b.WriteBatch = tx.tx.NewWriteBatch()
	b.Locker = &txBatchLocker{}
	b.tx = tx

	return b
}

func (tx *Tx) Index() int {
	return int(tx.index)
}
<|endoftext|>


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

// nvim-go is a msgpack remote plugin for Neovim
package main

import (
	"context"
	"log"
	"net/http"
	_ "net/http/pprof" // For pprof debugging.
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"nvim-go/autocmd"
	"nvim-go/command"
	"nvim-go/command/delve"
	buildctx "nvim-go/ctx"
	"nvim-go/logger"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"go.uber.org/zap"
)

const (
	EnvLogLevel = "NVIM_GO_LOG_LEVEL"
	EnvDebug    = "NVIM_GO_DEBUG"
	EnvPprof    = "NVIM_GO_PPROF"
)

var (
	logLevel = os.Getenv(EnvLogLevel)
	debug    = os.Getenv(EnvDebug) != ""
	pprof    = os.Getenv(EnvPprof) != ""
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger := logger.NewZapLogger(logLevel, debug).With(zap.Namespace("nvim-go"))
	undo := zap.RedirectStdLog(zapLogger)
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	registerFn := func(p *plugin.Plugin) error {
		buildctxt := buildctx.NewContext()
		delve.Register(p, buildctxt)
		c := command.Register(ctx, p, buildctxt)
		autocmd.Register(ctx, p, buildctxt, c)

		if debug {
			// starts the gops agent
			if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
				return err
			}

			if pprof {
				const addr = "localhost:14715" // (n: 14)vim-(g: 7)(o: 15)
				zapLogger.Debug("start the pprof debugging", zap.String("listen at", addr))

				// enable the report of goroutine blocking events
				runtime.SetBlockProfileRate(1)
				go log.Println(http.ListenAndServe(addr, nil))
			}
		}

		return nil
	}
	go plugin.Main(registerFn)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)

	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Debug("main", zap.String("interrupted %s signal", sig.String()))
			return
		}
	}
}
<commit_msg>cmd/nvim-go: remove nvim-go zap.Namespace<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// nvim-go is a msgpack remote plugin for Neovim
package main

import (
	"context"
	"log"
	"net/http"
	_ "net/http/pprof" // For pprof debugging.
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"nvim-go/autocmd"
	"nvim-go/command"
	"nvim-go/command/delve"
	buildctx "nvim-go/ctx"
	"nvim-go/logger"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"go.uber.org/zap"
)

const (
	EnvLogLevel = "NVIM_GO_LOG_LEVEL"
	EnvDebug    = "NVIM_GO_DEBUG"
	EnvPprof    = "NVIM_GO_PPROF"
)

var (
	logLevel = os.Getenv(EnvLogLevel)
	debug    = os.Getenv(EnvDebug) != ""
	pprof    = os.Getenv(EnvPprof) != ""
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger := logger.NewZapLogger(logLevel, debug)
	undo := zap.RedirectStdLog(zapLogger)
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	registerFn := func(p *plugin.Plugin) error {
		buildctxt := buildctx.NewContext()
		delve.Register(p, buildctxt)
		c := command.Register(ctx, p, buildctxt)
		autocmd.Register(ctx, p, buildctxt, c)

		if debug {
			// starts the gops agent
			if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
				return err
			}

			if pprof {
				const addr = "localhost:14715" // (n: 14)vim-(g: 7)(o: 15)
				zapLogger.Debug("start the pprof debugging", zap.String("listen at", addr))

				// enable the report of goroutine blocking events
				runtime.SetBlockProfileRate(1)
				go log.Println(http.ListenAndServe(addr, nil))
			}
		}

		return nil
	}
	go plugin.Main(registerFn)

	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)

	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Debug("main", zap.String("interrupted %s signal", sig.String()))
			return
		}
	}
}
<|endoftext|>


<commit_before><commit_msg>add out of band character for login prints<commit_after><|endoftext|>


<commit_before>package lfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

type CallbackReader struct {
	C         CopyCallback
	TotalSize int64
	ReadSize  int64
	io.Reader
}

type CopyCallback func(totalSize int64, readSoFar int64, readSinceLast int) error

func (w *CallbackReader) Read(p []byte) (int, error) {
	n, err := w.Reader.Read(p)

	if n > 0 {
		w.ReadSize += int64(n)
	}

	if err == nil && w.C != nil {
		err = w.C(w.TotalSize, w.ReadSize, n)
	}

	return n, err
}

func CopyWithCallback(writer io.Writer, reader io.Reader, totalSize int64, cb CopyCallback) (int64, error) {
	if cb == nil {
		return io.Copy(writer, reader)
	}

	cbReader := &CallbackReader{
		C:         cb,
		TotalSize: totalSize,
		Reader:    reader,
	}
	return io.Copy(writer, cbReader)
}

func CopyCallbackFile(event, filename string, index, totalFiles int) (CopyCallback, *os.File, error) {
	logPath := Config.Getenv("GIT_LFS_PROGRESS")
	if len(logPath) == 0 || len(filename) == 0 || len(event) == 0 {
		return nil, nil, nil
	}

	if !filepath.IsAbs(logPath) {
		return nil, nil, fmt.Errorf("GIT_LFS_PROGRESS must be an absolute path")
	}

	cbDir := filepath.Dir(logPath)
	if err := os.MkdirAll(cbDir, 0755); err != nil {
		return nil, nil, wrapProgressError(err, event, logPath)
	}

	file, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return nil, file, wrapProgressError(err, event, logPath)
	}

	var prevWritten int64

	cb := CopyCallback(func(total int64, written int64, current int) error {
		if written != prevWritten {
			_, err := file.Write([]byte(fmt.Sprintf("%s %d/%d %d/%d %s\n", event, index, totalFiles, written, total, filename)))
			file.Sync()
			prevWritten = written
			return wrapProgressError(err, event, logPath)
		}

		return nil
	})

	return cb, file, nil
}

func wrapProgressError(err error, event, filename string) error {
	if err != nil {
		return fmt.Errorf("Error writing Git LFS %s progress to %s: %s", event, filename, err.Error())
	}

	return nil
}

// Return whether a given filename passes the include / exclude path filters
// Only paths that are in includePaths and outside excludePaths are passed
// If includePaths is empty that filter always passes and the same with excludePaths
// Both path lists support wildcard matches
func FilenamePassesIncludeExcludeFilter(filename string, includePaths, excludePaths []string) bool {
	if len(includePaths) == 0 && len(excludePaths) == 0 {
		return true
	}

	// For Win32, because git reports files with / separators
	cleanfilename := filepath.Clean(filename)
	if len(includePaths) > 0 {
		matched := false
		for _, inc := range includePaths {
			matched, _ = filepath.Match(inc, filename)
			if !matched && IsWindows() {
				// Also Win32 match
				matched, _ = filepath.Match(inc, cleanfilename)
			}
			if !matched {
				// Also support matching a parent directory without a wildcard
				if strings.HasPrefix(cleanfilename, inc+string(filepath.Separator)) {
					matched = true
				}
			}
			if matched {
				break
			}

		}
		if !matched {
			return false
		}
	}

	if len(excludePaths) > 0 {
		for _, ex := range excludePaths {
			matched, _ := filepath.Match(ex, filename)
			if !matched && IsWindows() {
				// Also Win32 match
				matched, _ = filepath.Match(ex, cleanfilename)
			}
			if matched {
				return false
			}
			// Also support matching a parent directory without a wildcard
			if strings.HasPrefix(cleanfilename, ex+string(filepath.Separator)) {
				return false
			}

		}
	}

	return true
}

// Are we running on Windows? Need to handle some extra path shenanigans
func IsWindows() bool {
	return runtime.GOOS == "windows"
}
<commit_msg>Cache platform detection & make it a bit more useful in other cases<commit_after>package lfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

type CallbackReader struct {
	C         CopyCallback
	TotalSize int64
	ReadSize  int64
	io.Reader
}

type Platform int

const (
	PlatformWindows      = Platform(iota)
	PlatformLinux        = Platform(iota)
	PlatformOSX          = Platform(iota)
	PlatformOther        = Platform(iota) // most likely a *nix variant e.g. freebsd
	PlatformUndetermined = Platform(iota)
)

var currentPlatform = PlatformUndetermined

type CopyCallback func(totalSize int64, readSoFar int64, readSinceLast int) error

func (w *CallbackReader) Read(p []byte) (int, error) {
	n, err := w.Reader.Read(p)

	if n > 0 {
		w.ReadSize += int64(n)
	}

	if err == nil && w.C != nil {
		err = w.C(w.TotalSize, w.ReadSize, n)
	}

	return n, err
}

func CopyWithCallback(writer io.Writer, reader io.Reader, totalSize int64, cb CopyCallback) (int64, error) {
	if cb == nil {
		return io.Copy(writer, reader)
	}

	cbReader := &CallbackReader{
		C:         cb,
		TotalSize: totalSize,
		Reader:    reader,
	}
	return io.Copy(writer, cbReader)
}

func CopyCallbackFile(event, filename string, index, totalFiles int) (CopyCallback, *os.File, error) {
	logPath := Config.Getenv("GIT_LFS_PROGRESS")
	if len(logPath) == 0 || len(filename) == 0 || len(event) == 0 {
		return nil, nil, nil
	}

	if !filepath.IsAbs(logPath) {
		return nil, nil, fmt.Errorf("GIT_LFS_PROGRESS must be an absolute path")
	}

	cbDir := filepath.Dir(logPath)
	if err := os.MkdirAll(cbDir, 0755); err != nil {
		return nil, nil, wrapProgressError(err, event, logPath)
	}

	file, err := os.OpenFile(logPath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		return nil, file, wrapProgressError(err, event, logPath)
	}

	var prevWritten int64

	cb := CopyCallback(func(total int64, written int64, current int) error {
		if written != prevWritten {
			_, err := file.Write([]byte(fmt.Sprintf("%s %d/%d %d/%d %s\n", event, index, totalFiles, written, total, filename)))
			file.Sync()
			prevWritten = written
			return wrapProgressError(err, event, logPath)
		}

		return nil
	})

	return cb, file, nil
}

func wrapProgressError(err error, event, filename string) error {
	if err != nil {
		return fmt.Errorf("Error writing Git LFS %s progress to %s: %s", event, filename, err.Error())
	}

	return nil
}

// Return whether a given filename passes the include / exclude path filters
// Only paths that are in includePaths and outside excludePaths are passed
// If includePaths is empty that filter always passes and the same with excludePaths
// Both path lists support wildcard matches
func FilenamePassesIncludeExcludeFilter(filename string, includePaths, excludePaths []string) bool {
	if len(includePaths) == 0 && len(excludePaths) == 0 {
		return true
	}

	// For Win32, because git reports files with / separators
	cleanfilename := filepath.Clean(filename)
	if len(includePaths) > 0 {
		matched := false
		for _, inc := range includePaths {
			matched, _ = filepath.Match(inc, filename)
			if !matched && IsWindows() {
				// Also Win32 match
				matched, _ = filepath.Match(inc, cleanfilename)
			}
			if !matched {
				// Also support matching a parent directory without a wildcard
				if strings.HasPrefix(cleanfilename, inc+string(filepath.Separator)) {
					matched = true
				}
			}
			if matched {
				break
			}

		}
		if !matched {
			return false
		}
	}

	if len(excludePaths) > 0 {
		for _, ex := range excludePaths {
			matched, _ := filepath.Match(ex, filename)
			if !matched && IsWindows() {
				// Also Win32 match
				matched, _ = filepath.Match(ex, cleanfilename)
			}
			if matched {
				return false
			}
			// Also support matching a parent directory without a wildcard
			if strings.HasPrefix(cleanfilename, ex+string(filepath.Separator)) {
				return false
			}

		}
	}

	return true
}

func GetPlatform() Platform {
	if currentPlatform == PlatformUndetermined {
		switch runtime.GOOS {
		case "windows":
			currentPlatform = PlatformWindows
		case "linux":
			currentPlatform = PlatformLinux
		case "darwin":
			currentPlatform = PlatformOSX
		default:
			currentPlatform = PlatformOther
		}
	}
	return currentPlatform
}

// Are we running on Windows? Need to handle some extra path shenanigans
func IsWindows() bool {
	return GetPlatform() == PlatformWindows
}
<|endoftext|>


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

package lib

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/s3"
)

const CiphertextLength = 204

type BossVaultClient struct {
	kms *kmsClient
	s3  *s3Client
}

func NewBossVaultClient() *BossVaultClient {
	s := session.New()
	return &BossVaultClient{
		kms: &kmsClient{kms.New(s)},
		s3:  &s3Client{s3.New(s)},
	}
}

func (c *BossVaultClient) EncryptAndStore(bucket, artifact, content string) error {
	namespace, err := nsFrom(artifact)
	if err != nil {
		return err
	}

	plaintext, err := contentBytes(content)
	if err != nil {
		return err
	}

	keyId, err := c.kms.keyIdForAlias(namespace)
	if err != nil {
		return err
	}

	dk, err := c.kms.dataKey(keyId)
	if err != nil {
		return err
	}

	encrypted, err := encrypt(plaintext, dk.Plaintext)
	if err != nil {
		return err
	}

	return c.s3.store(artifact, bucket, encrypted, dk.CiphertextBlob)
}

func (c *BossVaultClient) RetrieveAndDecrypt(bucket, artifact string) ([]byte, error) {
	obj, err := c.s3.GetObject(
		&s3.GetObjectInput{
			Bucket: &bucket,
			Key:    &artifact,
		},
	)
	if err != nil {
		return nil, err
	}

	content, err := ioutil.ReadAll(obj.Body)
	if err != nil {
		return nil, err
	}

	encryptedKey := content[:CiphertextLength]
	payload := content[CiphertextLength:]

	dk, err := c.kms.Decrypt(
		&kms.DecryptInput{
			CiphertextBlob: encryptedKey,
		},
	)
	if err != nil {
		return nil, err
	}

	return decrypt(payload, dk.Plaintext)
}

type kmsClient struct {
	*kms.KMS
}

type s3Client struct {
	*s3.S3
}

func (kc *kmsClient) dataKey(keyId string) (out *kms.GenerateDataKeyOutput, err error) {
	keySpec := "AES_256"
	out, err = kc.GenerateDataKey(&kms.GenerateDataKeyInput{
		KeyId:   &keyId,
		KeySpec: &keySpec,
	})
	if err != nil {
		return
	}
	return
}

func (kc *kmsClient) keyIdForAlias(alias string) (string, error) {
	var keyId string
	var err error

	fullAlias := fmt.Sprintf("alias/%s", alias)

	err = kc.ListAliasesPages(
		&kms.ListAliasesInput{},
		func(out *kms.ListAliasesOutput, lastPage bool) bool {
			for _, a := range out.Aliases {
				if *a.AliasName == fullAlias {
					keyId = *a.TargetKeyId
					return true
				}
			}
			return lastPage
		},
	)
	if err != nil {
		return keyId, err
	}

	if keyId == "" {
		err = fmt.Errorf("No master key found with alias %s", alias)
	}

	return keyId, err
}

func (sc *s3Client) store(artifact, bucket string, encrypted, encryptedKey []byte) error {
	sse := "aws:kms"
	payload := append(encryptedKey, encrypted...)
	body := bytes.NewReader(payload)
	_, err := sc.PutObject(
		&s3.PutObjectInput{
			Bucket:               &bucket,
			Key:                  &artifact,
			Body:                 body,
			ServerSideEncryption: &sse,
		},
	)
	return err
}

func randomBytes(n int) ([]byte, error) {
	buf := make([]byte, n)
	if _, err := io.ReadFull(rand.Reader, buf); err != nil {
		return nil, err
	}
	return buf, nil
}

func contentBytes(content string) ([]byte, error) {
	var buf []byte
	var err error
	parts := strings.Split(content, "@")
	if len(parts) > 1 && parts[0] == "" && len(parts[1]) > 0 {
		path := parts[1]
		if file, err := os.Open(path); err != nil {
			return nil, err
		} else {
			defer file.Close()
			if buf, err = ioutil.ReadAll(file); err != nil {
				return nil, err
			}
		}
	} else {
		buf = []byte(content)
	}
	return buf, err
}

func nsFrom(artifact string) (ns string, err error) {
	parts := strings.Split(artifact, "/")
	if len(parts) == 1 {
		err = fmt.Errorf("Invalid artifact name")
	} else {
		ns = parts[0]
	}
	return ns, err
}

func encrypt(plaintext, key []byte) ([]byte, error) {
	var block cipher.Block
	var err error
	var iv []byte

	if block, err = aes.NewCipher(key); err != nil {
		return nil, err
	}

	if iv, err = randomBytes(aes.BlockSize); err != nil {
		return nil, err
	}

	ciphertext := make([]byte, len(plaintext))
	cfb := cipher.NewCFBEncrypter(block, iv)
	cfb.XORKeyStream(ciphertext, plaintext)

	return append(iv, ciphertext...), nil
}

func decrypt(ciphertext, key []byte) ([]byte, error) {
	iv := ciphertext[:aes.BlockSize]
	payload := ciphertext[aes.BlockSize:]
	decrypted := make([]byte, len(payload))

	if block, err := aes.NewCipher(key); err != nil {
		return nil, err
	} else {
		cfb := cipher.NewCFBDecrypter(block, iv)
		cfb.XORKeyStream(decrypted, payload)
	}

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

package lib

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/kms"
	"github.com/aws/aws-sdk-go/service/s3"
)

const CiphertextLength = 204

type BossVaultClient struct {
	kms *kmsClient
	s3  *s3Client
}

func NewBossVaultClient() *BossVaultClient {
	s := session.New()
	return &BossVaultClient{
		kms: &kmsClient{kms.New(s)},
		s3:  &s3Client{s3.New(s)},
	}
}

func (c *BossVaultClient) EncryptAndStore(bucket, artifact, content string) error {
	namespace, err := nsFrom(artifact)
	if err != nil {
		return err
	}

	plaintext, err := contentBytes(content)
	if err != nil {
		return err
	}

	keyId, err := c.kms.keyIdForAlias(namespace)
	if err != nil {
		return err
	}

	dk, err := c.kms.dataKey(keyId)
	if err != nil {
		return err
	}

	encrypted, err := encrypt(plaintext, dk.Plaintext)
	if err != nil {
		return err
	}

	return c.s3.store(artifact, bucket, encrypted, dk.CiphertextBlob)
}

func (c *BossVaultClient) RetrieveAndDecrypt(bucket, artifact string) ([]byte, error) {
	obj, err := c.s3.GetObject(
		&s3.GetObjectInput{
			Bucket: &bucket,
			Key:    &artifact,
		},
	)
	if err != nil {
		return nil, err
	}

	content, err := ioutil.ReadAll(obj.Body)
	if err != nil {
		return nil, err
	}

	encryptedKey := content[:CiphertextLength]
	payload := content[CiphertextLength:]

	dk, err := c.kms.Decrypt(
		&kms.DecryptInput{
			CiphertextBlob: encryptedKey,
		},
	)
	if err != nil {
		return nil, err
	}

	return decrypt(payload, dk.Plaintext)
}

type kmsClient struct {
	*kms.KMS
}

type s3Client struct {
	*s3.S3
}

func (kc *kmsClient) dataKey(keyId string) (out *kms.GenerateDataKeyOutput, err error) {
	keySpec := "AES_256"
	out, err = kc.GenerateDataKey(&kms.GenerateDataKeyInput{
		KeyId:   &keyId,
		KeySpec: &keySpec,
	})
	if err != nil {
		return
	}
	return
}

func (kc *kmsClient) keyIdForAlias(alias string) (string, error) {
	var keyId string
	var err error

	fullAlias := fmt.Sprintf("alias/%s", alias)

	err = kc.ListAliasesPages(
		&kms.ListAliasesInput{},
		func(out *kms.ListAliasesOutput, lastPage bool) bool {
			for _, a := range out.Aliases {
				if *a.AliasName == fullAlias {
					keyId = *a.TargetKeyId
					return true
				}
			}
			return lastPage
		},
	)
	if err != nil {
		return keyId, err
	}

	if keyId == "" {
		err = fmt.Errorf("No master key found with alias %s", alias)
	}

	return keyId, err
}

func (sc *s3Client) store(artifact, bucket string, encrypted, encryptedKey []byte) error {
	sse := "aws:kms"
	payload := append(encryptedKey, encrypted...)
	body := bytes.NewReader(payload)
	_, err := sc.PutObject(
		&s3.PutObjectInput{
			Bucket:               &bucket,
			Key:                  &artifact,
			Body:                 body,
			ServerSideEncryption: &sse,
		},
	)
	return err
}

func randomBytes(n int) ([]byte, error) {
	buf := make([]byte, n)
	if _, err := io.ReadFull(rand.Reader, buf); err != nil {
		return nil, err
	}
	return buf, nil
}

func contentBytes(content string) ([]byte, error) {
	var buf []byte
	var err error
	parts := strings.Split(content, "@")
	if len(parts) > 1 && parts[0] == "" && len(parts[1]) > 0 {
		path := parts[1]
		if file, err := os.Open(path); err != nil {
			return nil, err
		} else {
			defer file.Close()
			if buf, err = ioutil.ReadAll(file); err != nil {
				return nil, err
			}
		}
	} else {
		buf = []byte(content)
	}
	return buf, err
}

func nsFrom(artifact string) (ns string, err error) {
	parts := strings.Split(artifact, "/")
	if len(parts) == 1 {
		err = fmt.Errorf("Invalid artifact name")
	} else {
		ns = parts[0]
	}
	return ns, err
}

func encrypt(plaintext, key []byte) ([]byte, error) {
	var block cipher.Block
	var gcm cipher.AEAD
	var err error
	var nonce []byte

	if block, err = aes.NewCipher(key); err != nil {
		return nil, err
	}

	if gcm, err = cipher.NewGCM(block); err != nil {
		return nil, err
	}

	if nonce, err = randomBytes(gcm.NonceSize()); err != nil {
		return nil, err
	}

	return gcm.Seal(nonce, nonce, plaintext, nil), nil
}

func decrypt(ciphertext, key []byte) ([]byte, error) {
	var block cipher.Block
	var gcm cipher.AEAD
	var err error

	if block, err = aes.NewCipher(key); err != nil {
		return nil, err
	}

	if gcm, err = cipher.NewGCM(block); err != nil {
		return nil, err
	}

	nonceSize := gcm.NonceSize()
	nonce := ciphertext[:nonceSize]

	return gcm.Open(nil, nonce, ciphertext[nonceSize:], nil)
}
<|endoftext|>


<commit_before>// Copyright 2012, Google 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 vterrors provides helpers for propagating internal errors through the Vitess
// system (including across RPC boundaries) in a structured way.
package vterrors

import (
	"fmt"
	"sort"
	"strings"

	vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)

// ConcatenateErrors aggregates an array of errors into a single error by string concatenation.
func ConcatenateErrors(errors []error) error {
	errStrs := make([]string, 0, len(errors))
	for _, e := range errors {
		errStrs = append(errStrs, fmt.Sprintf("%v", e))
	}
	// sort the error strings so we always have deterministic ordering
	sort.Strings(errStrs)
	return fmt.Errorf("%v", strings.Join(errStrs, "\n"))
}

// VtError is implemented by any type that exposes a vtrpcpb.ErrorCode.
type VtError interface {
	VtErrorCode() vtrpcpb.ErrorCode
}

// RecoverVtErrorCode attempts to recover a vtrpcpb.ErrorCode from an error.
func RecoverVtErrorCode(err error) vtrpcpb.ErrorCode {
	if vtErr, ok := err.(VtError); ok {
		return vtErr.VtErrorCode()
	}
	return vtrpcpb.ErrorCode_UNKNOWN_ERROR
}

// VitessError is the error type that we use internally for passing structured errors.
type VitessError struct {
	// Error code of the Vitess error.
	Code vtrpcpb.ErrorCode
	// Error message that should be returned. This allows us to change an error message
	// without losing the underlying error. For example, if you have an error like
	// context.DeadlikeExceeded, you don't want to modify it - otherwise you would lose
	// the ability to programatically check for that error. However, you might want to
	// add some context to the error, giving you a message like "command failed: deadline exceeded".
	// To do that, you can create a NewVitessError to wrap the original error, but redefine
	// the error message.
	Message string
	err     error
}

// Error implements the error interface. It will return the redefined error message, if there
// is one. If there isn't, it will return the original error message.
func (e *VitessError) Error() string {
	if e.Message == "" {
		return fmt.Sprintf("%v", e.err)
	}
	return e.Message
}

// VtErrorCode returns the underlying Vitess error code.
func (e *VitessError) VtErrorCode() vtrpcpb.ErrorCode {
	return e.Code
}

// AsString returns a VitessError as a string, with more detailed information than Error().
func (e *VitessError) AsString() string {
	if e.Message != "" {
		return fmt.Sprintf("Code: %v, Message: %v, err: %v", e.Code, e.Message, e.err)
	}
	return fmt.Sprintf("Code: %v, err: %v", e.Code, e.err)
}

// NewVitessError returns a VitessError backed error with the given arguments.
// Useful for preserving an underlying error while creating a new error message.
func NewVitessError(code vtrpcpb.ErrorCode, err error, format string, args ...interface{}) error {
	return &VitessError{
		Code:    code,
		Message: fmt.Sprintf(format, args...),
		err:     err,
	}
}

// FromError returns a VitessError with the supplied error code by wrapping an
// existing error.
func FromError(code vtrpcpb.ErrorCode, err error) error {
	return &VitessError{
		Code: code,
		err:  err,
	}
}

// WithPrefix allows a string to be prefixed to an error, without chaining a new VitessError.
func WithPrefix(prefix string, in error) error {
	vtErr, ok := in.(*VitessError)
	if !ok {
		return fmt.Errorf("%s%s", prefix, in)
	}

	return &VitessError{
		Code:    vtErr.Code,
		err:     vtErr.err,
		Message: fmt.Sprintf("%s%s", prefix, vtErr.Error()),
	}
}

// WithSuffix allows a string to be suffixed to an error, without chaining a new VitessError.
func WithSuffix(in error, suffix string) error {
	vtErr, ok := in.(*VitessError)
	if !ok {
		return fmt.Errorf("%s%s", in, suffix)
	}

	return &VitessError{
		Code:    vtErr.Code,
		err:     vtErr.err,
		Message: fmt.Sprintf("%s%s", vtErr.Error(), suffix),
	}
}
<commit_msg>vterrors: Added an example to FromError().<commit_after>// Copyright 2012, Google 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 vterrors provides helpers for propagating internal errors through the Vitess
// system (including across RPC boundaries) in a structured way.
package vterrors

import (
	"fmt"
	"sort"
	"strings"

	vtrpcpb "github.com/youtube/vitess/go/vt/proto/vtrpc"
)

// ConcatenateErrors aggregates an array of errors into a single error by string concatenation.
func ConcatenateErrors(errors []error) error {
	errStrs := make([]string, 0, len(errors))
	for _, e := range errors {
		errStrs = append(errStrs, fmt.Sprintf("%v", e))
	}
	// sort the error strings so we always have deterministic ordering
	sort.Strings(errStrs)
	return fmt.Errorf("%v", strings.Join(errStrs, "\n"))
}

// VtError is implemented by any type that exposes a vtrpcpb.ErrorCode.
type VtError interface {
	VtErrorCode() vtrpcpb.ErrorCode
}

// RecoverVtErrorCode attempts to recover a vtrpcpb.ErrorCode from an error.
func RecoverVtErrorCode(err error) vtrpcpb.ErrorCode {
	if vtErr, ok := err.(VtError); ok {
		return vtErr.VtErrorCode()
	}
	return vtrpcpb.ErrorCode_UNKNOWN_ERROR
}

// VitessError is the error type that we use internally for passing structured errors.
type VitessError struct {
	// Error code of the Vitess error.
	Code vtrpcpb.ErrorCode
	// Error message that should be returned. This allows us to change an error message
	// without losing the underlying error. For example, if you have an error like
	// context.DeadlikeExceeded, you don't want to modify it - otherwise you would lose
	// the ability to programatically check for that error. However, you might want to
	// add some context to the error, giving you a message like "command failed: deadline exceeded".
	// To do that, you can create a NewVitessError to wrap the original error, but redefine
	// the error message.
	Message string
	err     error
}

// Error implements the error interface. It will return the redefined error message, if there
// is one. If there isn't, it will return the original error message.
func (e *VitessError) Error() string {
	if e.Message == "" {
		return fmt.Sprintf("%v", e.err)
	}
	return e.Message
}

// VtErrorCode returns the underlying Vitess error code.
func (e *VitessError) VtErrorCode() vtrpcpb.ErrorCode {
	return e.Code
}

// AsString returns a VitessError as a string, with more detailed information than Error().
func (e *VitessError) AsString() string {
	if e.Message != "" {
		return fmt.Sprintf("Code: %v, Message: %v, err: %v", e.Code, e.Message, e.err)
	}
	return fmt.Sprintf("Code: %v, err: %v", e.Code, e.err)
}

// FromError returns a VitessError with the supplied error code by wrapping an
// existing error.
// Use this method also when you want to create a VitessError without a custom
// message. For example:
//	 err := vterrors.FromError(vtrpcpb.ErrorCode_INTERNAL_ERROR,
//     errors.New("no valid endpoint"))
func FromError(code vtrpcpb.ErrorCode, err error) error {
	return &VitessError{
		Code: code,
		err:  err,
	}
}

// NewVitessError returns a VitessError backed error with the given arguments.
// Useful for preserving an underlying error while creating a new error message.
func NewVitessError(code vtrpcpb.ErrorCode, err error, format string, args ...interface{}) error {
	return &VitessError{
		Code:    code,
		Message: fmt.Sprintf(format, args...),
		err:     err,
	}
}

// WithPrefix allows a string to be prefixed to an error, without chaining a new VitessError.
func WithPrefix(prefix string, in error) error {
	vtErr, ok := in.(*VitessError)
	if !ok {
		return fmt.Errorf("%s%s", prefix, in)
	}

	return &VitessError{
		Code:    vtErr.Code,
		err:     vtErr.err,
		Message: fmt.Sprintf("%s%s", prefix, vtErr.Error()),
	}
}

// WithSuffix allows a string to be suffixed to an error, without chaining a new VitessError.
func WithSuffix(in error, suffix string) error {
	vtErr, ok := in.(*VitessError)
	if !ok {
		return fmt.Errorf("%s%s", in, suffix)
	}

	return &VitessError{
		Code:    vtErr.Code,
		err:     vtErr.err,
		Message: fmt.Sprintf("%s%s", vtErr.Error(), suffix),
	}
}
<|endoftext|>


<commit_before>// Package taskqueue provides the functionality for receiving, handling and executing tasks.
// This package provides the routines for the task- and queue-workers.
// Queue-workers are the go-routines that wait for entries in the Redis-lists,
// parse them and send them to the task-workers.
// Task-workers are the go-routines that finally execute the tasks that they receive
// from the queue-workers.
package taskqueue

import (
	"../config"
	"../output"
	"../stats"
	"encoding/json"
	"fmt"
	"github.com/fzzy/radix/redis"
	"os/exec"
	"strings"
	"sync"
)

// A queueTask is the task as it is enqueued in a Redis-list.
type queueTask struct {
	Args []string // list of arguments passed to the defined script/application
}

// execute executes the passed script/application with the arguments from the queueTask object.
func (q queueTask) execute(script string) error {
	out, err := exec.Command(script, q.Args...).Output()

	if len(out) != 0 && err == nil {
		err = fmt.Errorf("%s", out)
	}

	return err
}

// parseQueueTask parses the string-value from a Redis-list entry.
// It returns an object of queueTask and a possible error if parsing failed.
func parseQueueTask(value string) (task queueTask, err error) {
	reader := strings.NewReader(value)
	parser := json.NewDecoder(reader)
	err = parser.Decode(&task)
	return
}

// A Taskqueue offers routines for the task-workers and queue-workers.
// It also offers routines to set the config-, output- and stats-objects,
// which are used from the worker-routines.
type Taskqueue struct {
	waitGroup sync.WaitGroup // wait group used to handle a proper application shutdown, in case of any Redis (connection) errors
	config    config.Config  // config object, storing, for instance, connection data
	output    output.Output  // output object used to write debug-/error-messages, mostly for notifying about errors on task-execution
	stats     *stats.Stats   // stats object for gathering usage data
}

// New returns a new instance of a Taskqueue
func New() Taskqueue {
	return Taskqueue{}
}

// SetConfig sets the config object
func (tq *Taskqueue) SetConfig(c config.Config) {
	tq.config = c
}

// SetOutput sets the output object
func (tq *Taskqueue) SetOutput(o output.Output) {
	tq.output = o
}

// SetStats sets the stats object
func (tq *Taskqueue) SetStats(s *stats.Stats) {
	tq.stats = s
}

// CreateWorker creates all worker go-routines.
func (tq Taskqueue) CreateWorker(ct config.Task) {
	queue := make(chan queueTask)

	for i := 0; i < ct.Workers; i++ {
		tq.waitGroup.Add(1)
		go tq.taskWorker(ct, queue)
	}
	tq.output.Debug(fmt.Sprintf("Created %d workers for type %s", ct.Workers, ct.Type))

	tq.waitGroup.Add(1)
	go tq.queueWorker(ct, queue)
	tq.output.Debug(fmt.Sprintf("Created queue worker for type %s", ct.Type))
}

// Wait waits for the waitGroup to keep the application running, for as long as there
// are any go-routines active.
func (tq Taskqueue) Wait() {
	tq.waitGroup.Wait()
}

// queueWorker connects to Redis and listens to the Redis-list for the according config.Task.
// This routine gets entries from Redis, tries to parse them into queueTask and sends them
// to the according instances of taskWorker.
func (tq Taskqueue) queueWorker(ct config.Task, queue chan queueTask) {
	rc, err := redis.Dial(tq.config.RedisNetwork, tq.config.RedisAddress)
	if err != nil {
		tq.output.StopError(fmt.Sprintf("redis.Dial(): %s", err))
	}
	defer rc.Close()

	queueKey := tq.config.RedisQueueKey + ":" + ct.Type

	for {
		values, err := rc.Cmd("BLPOP", queueKey, 0).List()
		if err != nil {
			// Errors here will likely be connection errors. Therefore we'll just
			// notify about the error and break the loop, which will stop the QueueWorker
			// and all related taskWorker instances for this config.Task.
			msg := fmt.Sprintf("Redis Error:\n%s\nStopping task %s.", err, ct.Type)
			tq.output.NotifyError(msg)
			break
		}

		for _, value := range values {
			// BLPOP can return entries from multiple lists. It therefore includes the
			// list-name where the returned entry comes from, which we don't need, as we only have one list.
			// We only need the "real" entry, so we just skip that "value" from Redis.
			if value == queueKey {
				continue
			}

			tq.output.Debug(fmt.Sprintf("Task from redis for type %s with payload %s", ct.Type, value))

			task, err := parseQueueTask(value)
			if err != nil {
				// Errors from parseQueueTask will just result in a notification.
				// So we'll just skip this entry/task and continue with the next one.
				msg := fmt.Sprintf("parseQueueTask(): %s", err)
				tq.output.NotifyError(msg)
				continue
			}

			queue <- task
		}
	}

	close(queue)
	tq.waitGroup.Done()
}

// taskWorker waits for queueTask items and executes them. If they return an error,
// it the output object to notify about that error.
func (tq Taskqueue) taskWorker(ct config.Task, queue chan queueTask) {
	for task := range queue {
		tq.output.Debug(fmt.Sprintf("Executing task type %s with payload %s", ct.Type, task.Args))
		tq.stats.IncrTaskCount(ct.Type)

		err := task.execute(ct.Script)

		if err != nil {
			msg := fmt.Sprintf("%s %s\n\n%s", ct.Script, strings.Join(task.Args, " "), err)
			tq.output.NotifyError(msg)
		}
	}

	tq.waitGroup.Done()
}
<commit_msg>implemented possibility for graceful shutdown of worker-routines (issue #9) & fixed issues related to proper waitGroup handling<commit_after>// Package taskqueue provides the functionality for receiving, handling and executing tasks.
// This package provides the routines for the task- and queue-workers.
// Queue-workers are the go-routines that wait for entries in the Redis-lists,
// parse them and send them to the task-workers.
// Task-workers are the go-routines that finally execute the tasks that they receive
// from the queue-workers.
package taskqueue

import (
	"../config"
	"../output"
	"../stats"
	"encoding/json"
	"fmt"
	"github.com/fzzy/radix/redis"
	"os/exec"
	"strings"
	"sync"
)

// A queueTask is the task as it is enqueued in a Redis-list.
type queueTask struct {
	Args []string // list of arguments passed to the defined script/application
}

// execute executes the passed script/application with the arguments from the queueTask object.
func (q queueTask) execute(script string) error {
	out, err := exec.Command(script, q.Args...).Output()

	if len(out) != 0 && err == nil {
		err = fmt.Errorf("%s", out)
	}

	return err
}

// parseQueueTask parses the string-value from a Redis-list entry.
// It returns an object of queueTask and a possible error if parsing failed.
func parseQueueTask(value string) (task queueTask, err error) {
	reader := strings.NewReader(value)
	parser := json.NewDecoder(reader)
	err = parser.Decode(&task)
	return
}

// A Taskqueue offers routines for the task-workers and queue-workers.
// It also offers routines to set the config-, output- and stats-objects,
// which are used from the worker-routines.
type Taskqueue struct {
	waitGroup sync.WaitGroup // wait group used to handle a proper application shutdown
	config    config.Config  // config object, storing, for instance, connection data
	output    output.Output  // output object for handling debug-/error-messages and notifying about task execution errors
	stats     *stats.Stats   // stats object for gathering usage data
	quit      chan int       // channel used to gracefully shutdown all go-routines
}

// New returns a new instance of a Taskqueue
func New() Taskqueue {
	q := make(chan int)

	return Taskqueue{
		quit: q,
	}
}

// SetConfig sets the config object
func (tq *Taskqueue) SetConfig(c config.Config) {
	tq.config = c
}

// SetOutput sets the output object
func (tq *Taskqueue) SetOutput(o output.Output) {
	tq.output = o
}

// SetStats sets the stats object
func (tq *Taskqueue) SetStats(s *stats.Stats) {
	tq.stats = s
}

// CreateWorker creates all worker go-routines.
func (tq *Taskqueue) CreateWorker(ct config.Task) {
	queue := make(chan queueTask)

	for i := 0; i < ct.Workers; i++ {
		tq.waitGroup.Add(1)
		go tq.taskWorker(ct, queue)
	}
	tq.output.Debug(fmt.Sprintf("Created %d workers for type %s", ct.Workers, ct.Type))

	tq.waitGroup.Add(1)
	go tq.queueWorker(ct, queue)
	tq.output.Debug(fmt.Sprintf("Created queue worker for type %s", ct.Type))
}

// Wait waits for the waitGroup to keep the application running, for as long as there
// are any go-routines active.
func (tq *Taskqueue) Wait() {
	tq.waitGroup.Wait()
}

// Stop triggers the graceful shutdown of all worker-routines.
func (tq *Taskqueue) Stop() {
	close(tq.quit)
}

// queueWorker connects to Redis and listens to the Redis-list for the according config.Task.
// This routine gets entries from Redis, tries to parse them into queueTask and sends them
// to the according instances of taskWorker.
func (tq *Taskqueue) queueWorker(ct config.Task, queue chan queueTask) {
	rc, err := redis.Dial(tq.config.RedisNetwork, tq.config.RedisAddress)
	if err != nil {
		tq.output.StopError(fmt.Sprintf("redis.Dial(): %s", err))
	}
	defer rc.Close()

	queueKey := tq.config.RedisQueueKey + ":" + ct.Type

	// This go-routine waits for the quit-channel to close, which signals to shutdown of
	// all worker-routines. We archive that by closing the Redis-connection and catching that error.
	shutdown := false
	go func() {
		_, ok := <-tq.quit
		if !ok {
			shutdown = true
			rc.Close()
			tq.output.Debug(fmt.Sprintf("Shutting down workers for type %s", ct.Type))
		}
	}()

	for {
		values, err := rc.Cmd("BLPOP", queueKey, 0).List()
		if err != nil {
			// Errors here will likely be connection errors. Therefore we'll just
			// notify about the error and break the loop, which will stop the queueWorker
			// and all related taskWorker instances for this config.Task.
			// When shutdown == true, we're currently handling a graceful shutdown,
			// so we won't notify in that case and just break the loop.
			if shutdown == false {
				msg := fmt.Sprintf("Redis Error:\n%s\nStopping task %s.", err, ct.Type)
				tq.output.NotifyError(msg)
			}
			break
		}

		for _, value := range values {
			// BLPOP can return entries from multiple lists. It therefore includes the
			// list-name where the returned entry comes from, which we don't need, as we only have one list.
			// We only need the "real" entry, so we just skip that "value" from Redis.
			if value == queueKey {
				continue
			}

			tq.output.Debug(fmt.Sprintf("Task from redis for type %s with payload %s", ct.Type, value))

			task, err := parseQueueTask(value)
			if err != nil {
				// Errors from parseQueueTask will just result in a notification.
				// So we'll just skip this entry/task and continue with the next one.
				msg := fmt.Sprintf("parseQueueTask(): %s", err)
				tq.output.NotifyError(msg)
				continue
			}

			queue <- task
		}
	}

	close(queue)
	tq.waitGroup.Done()
}

// taskWorker waits for queueTask items and executes them. If they return an error,
// it the output object to notify about that error.
func (tq *Taskqueue) taskWorker(ct config.Task, queue chan queueTask) {
	for task := range queue {
		tq.output.Debug(fmt.Sprintf("Executing task type %s with payload %s", ct.Type, task.Args))
		tq.stats.IncrTaskCount(ct.Type)

		err := task.execute(ct.Script)

		if err != nil {
			msg := fmt.Sprintf("%s %s\n\n%s", ct.Script, strings.Join(task.Args, " "), err)
			tq.output.NotifyError(msg)
		}
	}

	tq.waitGroup.Done()
}
<|endoftext|>


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

package hangul

/*  Filename:    hangul_test.go
 *  Author:      Homin Lee <homin.lee@suapapa.net>
 *  Created:     2012-07-16 17:16:58.048792 +0900 KST
 *  Description: Main test file for hangul
 */

import "testing"

func TestIdx(t *testing.T) {
	if li, ok := leadIdx(Lead(T)); !ok || li != 16 {
		t.Errorf("Got %v, expect %v", li, 16) // 양
	}
}

func TestStroke(t *testing.T) {
	var i int
	var ss = []int{5, 5, 3, 0, 4, 5}
	for _, c := range "세상아 안녕" {
		if ss[i] != Stroke(c) {
			t.Errorf("%c Expected %d got %d", c, ss[i], Stroke(c))
		}
		i += 1
	}

	if c := Stroke(JJ); c != 6 {
		t.Errorf("Unexpected count %d for JJ", c)
	}
	if c := Stroke(YAE); c != 4 {
		t.Errorf("Unexpected count %d for YAE", c)
	}
	if c := Stroke(WAE); c != 5 {
		t.Errorf("Unexpected count %d for WAE", c)
	}
}

func TestJoin(t *testing.T) {
	if c := Join(LEAD_S, MEDIAL_EO, 0); c != 0xC11C {
		t.Errorf("Got %c(%v), expect %c(%v)", c, c, 0xC11C, 0xC11C) // 서
	}
	if c := Join(LEAD_ZS, MEDIAL_U, TAIL_L); c != 0xC6B8 {
		t.Errorf("Got %c, expect %c", c, 0xC6B8) // 울
	}
	if c := Join(LEAD_P, MEDIAL_YEO, TAIL_NG); c != 0xD3C9 {
		t.Errorf("Got %c, expect %c", c, 0xD3C9) // 평
	}
	if c := Join(LEAD_ZS, MEDIAL_YA, TAIL_NG); c != 0xC591 {
		t.Errorf("Got %c, expect %c", c, 0xC11C) // 양
	}
}

func TestSplit(t *testing.T) {
	type hangul struct {
		i, m, f rune
	}

	var splitted = []hangul{
		{J, A, 0},    // 자
		{M, O, 0},    // 모
		{H, A, N},    // 한
		{G, EU, L},   // 글
		{ZS, A, N},   // 안
		{N, YEO, NG}, // 녕
	}

	var idx int
	for _, c := range "자모한글안녕" {
		exp := splitted[idx]
		i, m, f := SplitCompat(c)
		switch {
		case i != exp.i:
			t.Errorf("%c-l: expected %c, got %c", c, exp.i, i)
			return
		case m != exp.m:
			t.Errorf("%c-m: expected %c, got %c", c, exp.m, m)
			return
		case f != exp.f:
			t.Errorf("%c-t: expected %c, got %c", c, exp.f, f)
			return

		}
		idx += 1
	}
}

func TestMultiElements(t *testing.T) {
	if es, ok := SplitMultiElement(GG); ok {
		if len(es) != 2 {
			t.Errorf("GG != G, G??\n")
		}
		if es[0] != G || es[1] != G {
			t.Errorf("%v\n", es)
		}
	} else {
		t.Errorf("Failed to get multielements\n")
	}

	if _, ok := SplitMultiElement(G); ok {
		t.Errorf("G is not multi element\n")
	}
}

func TestComaptJamo(t *testing.T) {
	if c := CompatJamo(LEAD_G); c != G {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", G, c)
	}
	if c := CompatJamo(TAIL_H); c != H {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", H, c)
	}
	if c := CompatJamo(MEDIAL_A); c != A {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", A, c)
	}
	if c := CompatJamo(MEDIAL_I); c != I {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", I, c)
	}
}

func TestJamoConstants(t *testing.T) {
	if H != 0x314E {
		t.Errorf("Last Jaeum sholud be 0x314E."+
			" not 0x%04x\n", H)
	}

	if I != 0x3163 {
		t.Errorf("Last Moeum sholud be 0x3163."+
			" not 0x%04x\n", I)
	}

	if LEAD_H != 0x1112 {
		t.Errorf("Last Lead sholud be 0x1112."+
			" not 0x%04x\n", LEAD_H)
	}

	if MEDIAL_I != 0x1175 {
		t.Errorf("Last Medial sholud be 0x1175."+
			" not 0x%04x\n", MEDIAL_I)
	}

	if TAIL_H != 0x11C2 {
		t.Errorf("Last Tail sholud be 0x11C3."+
			" not 0x%04x\n", TAIL_H)
	}
}

func TestEndsWithConsonant(t *testing.T) {
	cases := []struct {
		word     string
		endsWith bool
	}{
		{"", false},
		{"강", true},
		{"그날맑", true},
		{"이", false},
		{"물고기", false},
	}
	for _, c := range cases {
		if EndsWithConsonant(c.word) != c.endsWith {
			if c.endsWith {
				t.Errorf("'%s' should be ends with consonant",
					c.word)
			} else {
				t.Errorf("'%s' should not be ends with consonant",
					c.word)
			}
		}
	}
}

func TestAppendPostposition(t *testing.T) {
	cases := []struct {
		word     string
		with     string
		without  string
		expected string
	}{
		{"", "", "", ""},
		{"", "이", "가", "가"},
		{"칸", "은", "는", "칸은"},
		{"그", "은", "는", "그는"},
		{"그날맑", "이", "가", "그날맑이"},
		{"스무디", "을", "를", "스무디를"},
		{"영철", "이랑", "랑", "영철이랑"},
		{"순희", "이랑", "랑", "순희랑"},
	}
	for _, c := range cases {
		actual := AppendPostposition(c.word, c.with, c.without)
		if actual != c.expected {
			t.Errorf("'%s' should be '%s'", actual, c.expected)
		}
	}
}
<commit_msg>Add error case for AppendPostposition<commit_after>// Copyright 2012, Homin Lee. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package hangul

/*  Filename:    hangul_test.go
 *  Author:      Homin Lee <homin.lee@suapapa.net>
 *  Created:     2012-07-16 17:16:58.048792 +0900 KST
 *  Description: Main test file for hangul
 */

import "testing"

func TestIdx(t *testing.T) {
	if li, ok := leadIdx(Lead(T)); !ok || li != 16 {
		t.Errorf("Got %v, expect %v", li, 16) // 양
	}
}

func TestStroke(t *testing.T) {
	var i int
	var ss = []int{5, 5, 3, 0, 4, 5}
	for _, c := range "세상아 안녕" {
		if ss[i] != Stroke(c) {
			t.Errorf("%c Expected %d got %d", c, ss[i], Stroke(c))
		}
		i += 1
	}

	if c := Stroke(JJ); c != 6 {
		t.Errorf("Unexpected count %d for JJ", c)
	}
	if c := Stroke(YAE); c != 4 {
		t.Errorf("Unexpected count %d for YAE", c)
	}
	if c := Stroke(WAE); c != 5 {
		t.Errorf("Unexpected count %d for WAE", c)
	}
}

func TestJoin(t *testing.T) {
	if c := Join(LEAD_S, MEDIAL_EO, 0); c != 0xC11C {
		t.Errorf("Got %c(%v), expect %c(%v)", c, c, 0xC11C, 0xC11C) // 서
	}
	if c := Join(LEAD_ZS, MEDIAL_U, TAIL_L); c != 0xC6B8 {
		t.Errorf("Got %c, expect %c", c, 0xC6B8) // 울
	}
	if c := Join(LEAD_P, MEDIAL_YEO, TAIL_NG); c != 0xD3C9 {
		t.Errorf("Got %c, expect %c", c, 0xD3C9) // 평
	}
	if c := Join(LEAD_ZS, MEDIAL_YA, TAIL_NG); c != 0xC591 {
		t.Errorf("Got %c, expect %c", c, 0xC11C) // 양
	}
}

func TestSplit(t *testing.T) {
	type hangul struct {
		i, m, f rune
	}

	var splitted = []hangul{
		{J, A, 0},    // 자
		{M, O, 0},    // 모
		{H, A, N},    // 한
		{G, EU, L},   // 글
		{ZS, A, N},   // 안
		{N, YEO, NG}, // 녕
	}

	var idx int
	for _, c := range "자모한글안녕" {
		exp := splitted[idx]
		i, m, f := SplitCompat(c)
		switch {
		case i != exp.i:
			t.Errorf("%c-l: expected %c, got %c", c, exp.i, i)
			return
		case m != exp.m:
			t.Errorf("%c-m: expected %c, got %c", c, exp.m, m)
			return
		case f != exp.f:
			t.Errorf("%c-t: expected %c, got %c", c, exp.f, f)
			return

		}
		idx += 1
	}
}

func TestMultiElements(t *testing.T) {
	if es, ok := SplitMultiElement(GG); ok {
		if len(es) != 2 {
			t.Errorf("GG != G, G??\n")
		}
		if es[0] != G || es[1] != G {
			t.Errorf("%v\n", es)
		}
	} else {
		t.Errorf("Failed to get multielements\n")
	}

	if _, ok := SplitMultiElement(G); ok {
		t.Errorf("G is not multi element\n")
	}
}

func TestComaptJamo(t *testing.T) {
	if c := CompatJamo(LEAD_G); c != G {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", G, c)
	}
	if c := CompatJamo(TAIL_H); c != H {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", H, c)
	}
	if c := CompatJamo(MEDIAL_A); c != A {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", A, c)
	}
	if c := CompatJamo(MEDIAL_I); c != I {
		t.Errorf("Failed to convert to comaptibility jamo! ")
		t.Errorf("expected %v but, got %v\n", I, c)
	}
}

func TestJamoConstants(t *testing.T) {
	if H != 0x314E {
		t.Errorf("Last Jaeum sholud be 0x314E."+
			" not 0x%04x\n", H)
	}

	if I != 0x3163 {
		t.Errorf("Last Moeum sholud be 0x3163."+
			" not 0x%04x\n", I)
	}

	if LEAD_H != 0x1112 {
		t.Errorf("Last Lead sholud be 0x1112."+
			" not 0x%04x\n", LEAD_H)
	}

	if MEDIAL_I != 0x1175 {
		t.Errorf("Last Medial sholud be 0x1175."+
			" not 0x%04x\n", MEDIAL_I)
	}

	if TAIL_H != 0x11C2 {
		t.Errorf("Last Tail sholud be 0x11C3."+
			" not 0x%04x\n", TAIL_H)
	}
}

func TestEndsWithConsonant(t *testing.T) {
	cases := []struct {
		word     string
		endsWith bool
	}{
		{"", false},
		{"강", true},
		{"그날맑", true},
		{"이", false},
		{"물고기", false},
	}
	for _, c := range cases {
		if EndsWithConsonant(c.word) != c.endsWith {
			if c.endsWith {
				t.Errorf("'%s' should be ends with consonant",
					c.word)
			} else {
				t.Errorf("'%s' should not be ends with consonant",
					c.word)
			}
		}
	}
}

func TestAppendPostposition(t *testing.T) {
	cases := []struct {
		word     string
		with     string
		without  string
		expected string
	}{
		{"", "", "", ""},
		{"", "이", "가", "가"},
		{"칸", "은", "는", "칸은"},
		{"그", "은", "는", "그는"},
		{"그날맑", "이", "가", "그날맑이"},
		{"스무디", "을", "를", "스무디를"},
		{"영철", "이랑", "랑", "영철이랑"},
		{"순희", "이랑", "랑", "순희랑"},
		{"마을", "으로", "로", "마을로"},
	}
	for _, c := range cases {
		actual := AppendPostposition(c.word, c.with, c.without)
		if actual != c.expected {
			t.Errorf("'%s' should be '%s'", actual, c.expected)
		}
	}
}
<|endoftext|>