<commit_before>package m_etcd_test

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

	"github.com/lytics/metafora"
	"github.com/lytics/metafora/m_etcd"
	"github.com/lytics/metafora/m_etcd/testutil"
	"github.com/lytics/metafora/statemachine"
)

// TestNew is an integration test for m_etcd's New function.
func TestNew(t *testing.T) {
	t.Parallel()
	etcdc := testutil.NewEtcdClient(t)

	const recursive = true
	etcdc.Delete("testnew1", recursive)
	etcdc.Delete("testnew2", recursive)

	// Start 2 simple consumers in different namespaces
	h1 := func(_ string, cmds <-chan statemachine.Message) statemachine.Message {
		return <-cmds
	}
	coord1, hf1, bal1 := m_etcd.New("node1", "testnew1", etcdc, h1)
	cons1, err := metafora.NewConsumer(coord1, hf1, bal1)
	if err != nil {
		t.Fatalf("Error creating consumer 1: %v", err)
	}
	done1 := make(chan bool)
	go func() {
		defer close(done1)
		cons1.Run()
	}()

	h2 := func(_ string, cmds <-chan statemachine.Message) statemachine.Message {
		return <-cmds
	}
	coord2, hf2, bal2 := m_etcd.New("node1", "testnew2", etcdc, h2)
	cons2, err := metafora.NewConsumer(coord2, hf2, bal2)
	if err != nil {
		t.Fatalf("Error creating consumer 1: %v", err)
	}
	done2 := make(chan bool)
	go func() {
		defer close(done2)
		cons2.Run()
	}()

	// Create clients and start some tests
	cli1 := m_etcd.NewClient("testnew1", etcdc)
	cli2 := m_etcd.NewClient("testnew2", etcdc)

	if err := cli1.SubmitTask("task1"); err != nil {
		t.Fatalf("Error submitting task1: %v", err)
	}
	if err := cli2.SubmitTask("task2"); err != nil {
		t.Fatalf("Error submitting task2: %v", err)
	}

	// Give consumers a bit to pick up tasks
	time.Sleep(250 * time.Millisecond)

	{
		tasks := cons1.Tasks()
		if len(tasks) != 1 || tasks[0].ID() != "task1" || !tasks[0].Stopped().IsZero() {
			buf, _ := json.Marshal(tasks)
			t.Fatalf("Expected task1 to be running but found: %s", string(buf))
		}
	}

	{
		tasks := cons2.Tasks()
		if len(tasks) != 1 || tasks[0].ID() != "task2" || !tasks[0].Stopped().IsZero() {
			buf, _ := json.Marshal(tasks)
			t.Fatalf("Expected task2 to be running but found: %s", string(buf))
		}
	}

	// Kill task1
	cmdr := m_etcd.NewCommander("testnew1", etcdc)
	if err := cmdr.Send("task1", statemachine.Message{Code: statemachine.Kill}); err != nil {
		t.Fatalf("Error sending kill to task1: %v", err)
	}
	time.Sleep(250 * time.Millisecond)

	{
		tasks := cons1.Tasks()
		if len(tasks) != 0 {
			buf, _ := json.Marshal(tasks)
			t.Fatalf("Expected no tasks but found: %s", string(buf))
		}
	}

	{
		tasks := cons2.Tasks()
		if len(tasks) != 1 || tasks[0].ID() != "task2" || !tasks[0].Stopped().IsZero() {
			buf, _ := json.Marshal(tasks)
			t.Fatalf("Expected task2 to be running but found: %s", string(buf))
		}
	}

	// Shutdown both consumers
	cons1.Shutdown()
	cons2.Shutdown()
	<-done1
	<-done2
}
<commit_msg>Dramatically expand etcd integration test (WIP)<commit_after>package m_etcd_test

import (
	"errors"
	"testing"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/lytics/metafora"
	"github.com/lytics/metafora/m_etcd"
	"github.com/lytics/metafora/m_etcd/testutil"
	"github.com/lytics/metafora/statemachine"
)

// TestNew is an integration test for m_etcd's New function.
func TestNew(t *testing.T) {
	t.Parallel()
	etcdc := testutil.NewEtcdClient(t)

	const recursive = true
	etcdc.Delete("test-a", recursive)
	etcdc.Delete("test-b", recursive)

	h := func(tid string, cmds <-chan statemachine.Message) statemachine.Message {
		cmd := <-cmds
		if tid == "error-test" {
			return statemachine.Message{Code: statemachine.Error, Err: errors.New("error-test")}
		}
		return cmd
	}

	newC := func(name, ns string) *metafora.Consumer {
		coord, hf, bal := m_etcd.New(name, ns, etcdc, h)
		cons, err := metafora.NewConsumer(coord, hf, bal)
		if err != nil {
			t.Fatalf("Error creating consumer %s:%s: %v", ns, name, err)
		}
		go cons.Run()
		return cons
	}
	// Start 4 consumers, 2 per namespace
	cons1a := newC("node1", "test-a")
	cons2a := newC("node2", "test-a")
	cons1b := newC("node1", "test-b")
	cons2b := newC("node2", "test-b")

	// Create clients and start some tests
	cliA := m_etcd.NewClient("test-a", etcdc)
	cliB := m_etcd.NewClient("test-b", etcdc)

	if err := cliA.SubmitTask("task1"); err != nil {
		t.Fatalf("Error submitting task1 to a: %v", err)
	}
	if err := cliB.SubmitTask("task1"); err != nil {
		t.Fatalf("Error submitting task1 to b: %v", err)
	}

	// Give consumers a bit to pick up tasks
	time.Sleep(250 * time.Millisecond)

	assertRunning := func(tid string, cons ...*metafora.Consumer) {
		found := false
		for _, c := range cons {
			tasks := c.Tasks()
			if len(tasks) > 0 && found {
				t.Fatal("Task already found running but another task is running on a different consumer")
			}
			if len(tasks) > 1 {
				t.Fatalf("Expected at most 1 task, but found: %d", len(tasks))
			}
			if len(tasks) == 1 && tasks[0].ID() == tid {
				found = true
			}
		}
		if !found {
			t.Fatalf("Could not find task=%q", tid)
		}
	}

	assertRunning("task1", cons1a, cons2a)
	assertRunning("task1", cons1b, cons2b)

	// Kill task1 in A
	{
		cmdr := m_etcd.NewCommander("test-a", etcdc)
		if err := cmdr.Send("task1", statemachine.Message{Code: statemachine.Kill}); err != nil {
			t.Fatalf("Error sending kill to task1: %v", err)
		}
		time.Sleep(250 * time.Millisecond)

		for _, c := range []*metafora.Consumer{cons1a, cons2a} {
			tasks := c.Tasks()
			if len(tasks) != 0 {
				t.Fatalf("Expected no tasks but found: %d", len(tasks))
			}
		}
	}

	// Submit a bunch of tasks to A
	{
		tasks := map[string]int{"task2": 1, "task3": 1, "task4": 1, "task5": 1, "task6": 1, "task7": 1}
		for tid := range tasks {
			if err := cliA.SubmitTask(tid); err != nil {
				t.Fatalf("Error submitting task=%q to a: %v", tid, err)
			}
		}

		// Give them time to start
		time.Sleep(500 * time.Millisecond)

		// Ensure they're balanced
		if err := cliA.SubmitCommand("node1", metafora.CommandBalance()); err != nil {
			t.Fatalf("Error submitting balance command to cons1a: %v", err)
		}
		time.Sleep(500 * time.Millisecond)
		if err := cliA.SubmitCommand("node2", metafora.CommandBalance()); err != nil {
			t.Fatalf("Error submitting balance command to cons1a: %v", err)
		}

		a1tasks := cons1a.Tasks()
		a2tasks := cons2a.Tasks()
		for _, task := range a1tasks {
			metafora.Debug("A1: ", task.ID(), " - ", task.Stopped().IsZero())
		}
		for _, task := range a2tasks {
			metafora.Debug("A2: ", task.ID(), " - ", task.Stopped().IsZero())
		}
		time.Sleep(500 * time.Millisecond)

		a1tasks = cons1a.Tasks()
		a2tasks = cons2a.Tasks()
		if len(a1tasks) < 2 || len(a1tasks) > 4 || len(a2tasks) < 2 || len(a2tasks) > 4 {
			t.Fatalf("Namespace A isn't fairly balanced: node1: %d; node2: %d", len(a1tasks), len(a2tasks))
		}

		// Shutting down a consumer should migrate all tasks to the other
		cons1a.Shutdown()
		time.Sleep(500 * time.Millisecond)

		a2tasks = cons2a.Tasks()
		if len(a2tasks) != len(tasks) {
			t.Fatalf("Consumer 2a should have received all %d tasks but only has %d.", len(tasks), len(a2tasks))
		}
	}

	// Shutdown
	cons2a.Shutdown()
	cons1b.Shutdown()
	cons2b.Shutdown()

	// Make sure everything is cleaned up
	respA, err := etcdc.Get("/test-a/tasks", true, true)
	if err != nil {
		t.Fatalf("Error getting tasks from etcd: %v", err)
	}
	respB, err := etcdc.Get("/test-b/tasks", true, true)
	if err != nil {
		t.Fatalf("Error getting tasks from etcd: %v", err)
	}

	nodes := []*etcd.Node{}
	nodes = append(nodes, respA.Node.Nodes...)
	nodes = append(nodes, respB.Node.Nodes...)
	for _, node := range nodes {
		if len(node.Nodes) > 0 {
			t.Fatalf("%s has %d nodes. First key: %s", node.Key, len(node.Nodes), node.Nodes[0].Key)
		}
	}
}
<|endoftext|>


<commit_before>package fasthttp

import (
	"bytes"
	"errors"
	"time"
)

// CookieExpireDelete may be set on Cookie.Expire for expiring the given cookie.
var CookieExpireDelete = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)

// Cookie represents HTTP response cookie.
type Cookie struct {
	// Key is cookie name.
	Key []byte

	// Value is cookie value.
	Value []byte

	// Expiration time for the cookie.
	//
	// Set expiration time to CookieExpireDelete for expiring (deleting)
	// the cookie on the client.
	//
	// By default cookie lifetime is limited by browser session.
	Expire time.Time

	// Domain for the cookie.
	//
	// By default cookie is set to the current domain.
	Domain []byte

	// Path for the cookie.
	//
	// By default cookie is set to the current page only.
	Path []byte

	bufKV argsKV
}

var zeroTime time.Time

// Clear clears the cookie.
func (c *Cookie) Clear() {
	c.Key = c.Key[:0]
	c.Value = c.Value[:0]
	c.Expire = zeroTime
	c.Domain = c.Domain[:0]
	c.Path = c.Path[:0]
}

// AppendBytes appends cookie representation to dst and returns dst
// (maybe newly allocated).
func (c *Cookie) AppendBytes(dst []byte) []byte {
	if len(c.Key) > 0 {
		dst = appendQuotedArg(dst, c.Key)
		dst = append(dst, '=')
	}
	dst = appendQuotedArg(dst, c.Value)

	if !c.Expire.IsZero() {
		c.bufKV.value = c.Expire.In(gmtLocation).AppendFormat(c.bufKV.value[:0], time.RFC1123)
		dst = append(dst, ';', ' ')
		dst = append(dst, strCookieExpires...)
		dst = append(dst, '=')
		dst = append(dst, c.bufKV.value...)
	}
	if len(c.Domain) > 0 {
		dst = appendCookiePart(dst, strCookieDomain, c.Domain)
	}
	if len(c.Path) > 0 {
		dst = appendCookiePart(dst, strCookiePath, c.Path)
	}
	return dst
}

var errNoCookies = errors.New("no cookies found")

// Parse parses Set-Cookie header.
func (c *Cookie) Parse(src []byte) error {
	c.Clear()

	var s cookieScanner
	s.b = src

	kv := &c.bufKV
	if !s.next(kv, true) {
		return errNoCookies
	}

	c.Key = append(c.Key[:0], kv.key...)
	c.Value = append(c.Value[:0], kv.value...)

	for s.next(kv, false) {
		if len(kv.key) == 0 && len(kv.value) == 0 {
			continue
		}
		switch {
		case bytes.Equal(strCookieExpires, kv.key):
			v := unsafeBytesToStr(kv.value)
			exptime, err := time.Parse(time.RFC1123, v)
			if err != nil {
				return err
			}
			c.Expire = exptime
		case bytes.Equal(strCookieDomain, kv.key):
			c.Domain = append(c.Domain[:0], kv.value...)
		case bytes.Equal(strCookiePath, kv.key):
			c.Path = append(c.Path[:0], kv.value...)
		}
	}
	return nil
}

func appendCookiePart(dst, key, value []byte) []byte {
	dst = append(dst, ';', ' ')
	dst = append(dst, key...)
	dst = append(dst, '=')
	return append(dst, value...)
}

func getCookieKey(dst, src []byte) []byte {
	n := bytes.IndexByte(src, '=')
	if n >= 0 {
		src = src[:n]
	}
	return decodeCookieArg(dst[:0], src, true)
}

func appendRequestCookieBytes(dst []byte, cookies []argsKV) []byte {
	for i, n := 0, len(cookies); i < n; i++ {
		kv := &cookies[i]
		if len(kv.key) > 0 {
			dst = appendQuotedArg(dst, kv.key)
			dst = append(dst, '=')
		}
		dst = appendQuotedArg(dst, kv.value)
		if i+1 < n {
			dst = append(dst, ';', ' ')
		}
	}
	return dst
}

func parseRequestCookies(cookies []argsKV, src []byte, kv *argsKV) []argsKV {
	var s cookieScanner
	s.b = src
	for s.next(kv, true) {
		if len(kv.key) > 0 || len(kv.value) > 0 {
			cookies = setArg(cookies, kv.key, kv.value)
		}
	}
	return cookies
}

type cookieScanner struct {
	b []byte
}

func (s *cookieScanner) next(kv *argsKV, decode bool) bool {
	if len(s.b) == 0 {
		return false
	}

	var b []byte
	n := bytes.IndexByte(s.b, ';')
	if n < 0 {
		b = s.b
		s.b = s.b[len(s.b):]
	} else {
		b = s.b[:n]
		s.b = s.b[n+1:]
	}

	n = bytes.IndexByte(b, '=')
	if n < 0 {
		kv.key = kv.key[:0]
		kv.value = decodeCookieArg(kv.value[:0], b, decode)
		return true
	}

	kv.key = decodeCookieArg(kv.key[:0], b[:n], decode)
	kv.value = decodeCookieArg(kv.value[:0], b[n+1:], decode)
	return true
}

func decodeCookieArg(dst, src []byte, decode bool) []byte {
	for len(src) > 0 && src[0] == ' ' {
		src = src[1:]
	}
	for len(src) > 0 && src[len(src)-1] == ' ' {
		src = src[:len(src)-1]
	}
	if !decode {
		return append(dst, src...)
	}
	return decodeArg(dst, src, true)
}
<commit_msg>Eliminated redundant memory allocation during cookie expiration time parsing<commit_after>package fasthttp

import (
	"bytes"
	"errors"
	"time"
)

// CookieExpireDelete may be set on Cookie.Expire for expiring the given cookie.
var CookieExpireDelete = time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)

// Cookie represents HTTP response cookie.
type Cookie struct {
	// Key is cookie name.
	Key []byte

	// Value is cookie value.
	Value []byte

	// Expiration time for the cookie.
	//
	// Set expiration time to CookieExpireDelete for expiring (deleting)
	// the cookie on the client.
	//
	// By default cookie lifetime is limited by browser session.
	Expire time.Time

	// Domain for the cookie.
	//
	// By default cookie is set to the current domain.
	Domain []byte

	// Path for the cookie.
	//
	// By default cookie is set to the current page only.
	Path []byte

	bufKV argsKV
}

var zeroTime time.Time

// Clear clears the cookie.
func (c *Cookie) Clear() {
	c.Key = c.Key[:0]
	c.Value = c.Value[:0]
	c.Expire = zeroTime
	c.Domain = c.Domain[:0]
	c.Path = c.Path[:0]
}

// AppendBytes appends cookie representation to dst and returns dst
// (maybe newly allocated).
func (c *Cookie) AppendBytes(dst []byte) []byte {
	if len(c.Key) > 0 {
		dst = appendQuotedArg(dst, c.Key)
		dst = append(dst, '=')
	}
	dst = appendQuotedArg(dst, c.Value)

	if !c.Expire.IsZero() {
		c.bufKV.value = c.Expire.In(gmtLocation).AppendFormat(c.bufKV.value[:0], time.RFC1123)
		dst = append(dst, ';', ' ')
		dst = append(dst, strCookieExpires...)
		dst = append(dst, '=')
		dst = append(dst, c.bufKV.value...)
	}
	if len(c.Domain) > 0 {
		dst = appendCookiePart(dst, strCookieDomain, c.Domain)
	}
	if len(c.Path) > 0 {
		dst = appendCookiePart(dst, strCookiePath, c.Path)
	}
	return dst
}

var errNoCookies = errors.New("no cookies found")

// Parse parses Set-Cookie header.
func (c *Cookie) Parse(src []byte) error {
	c.Clear()

	var s cookieScanner
	s.b = src

	kv := &c.bufKV
	if !s.next(kv, true) {
		return errNoCookies
	}

	c.Key = append(c.Key[:0], kv.key...)
	c.Value = append(c.Value[:0], kv.value...)

	for s.next(kv, false) {
		if len(kv.key) == 0 && len(kv.value) == 0 {
			continue
		}
		switch {
		case bytes.Equal(strCookieExpires, kv.key):
			v := unsafeBytesToStr(kv.value)
			exptime, err := time.ParseInLocation(time.RFC1123, v, gmtLocation)
			if err != nil {
				return err
			}
			c.Expire = exptime
		case bytes.Equal(strCookieDomain, kv.key):
			c.Domain = append(c.Domain[:0], kv.value...)
		case bytes.Equal(strCookiePath, kv.key):
			c.Path = append(c.Path[:0], kv.value...)
		}
	}
	return nil
}

func appendCookiePart(dst, key, value []byte) []byte {
	dst = append(dst, ';', ' ')
	dst = append(dst, key...)
	dst = append(dst, '=')
	return append(dst, value...)
}

func getCookieKey(dst, src []byte) []byte {
	n := bytes.IndexByte(src, '=')
	if n >= 0 {
		src = src[:n]
	}
	return decodeCookieArg(dst[:0], src, true)
}

func appendRequestCookieBytes(dst []byte, cookies []argsKV) []byte {
	for i, n := 0, len(cookies); i < n; i++ {
		kv := &cookies[i]
		if len(kv.key) > 0 {
			dst = appendQuotedArg(dst, kv.key)
			dst = append(dst, '=')
		}
		dst = appendQuotedArg(dst, kv.value)
		if i+1 < n {
			dst = append(dst, ';', ' ')
		}
	}
	return dst
}

func parseRequestCookies(cookies []argsKV, src []byte, kv *argsKV) []argsKV {
	var s cookieScanner
	s.b = src
	for s.next(kv, true) {
		if len(kv.key) > 0 || len(kv.value) > 0 {
			cookies = setArg(cookies, kv.key, kv.value)
		}
	}
	return cookies
}

type cookieScanner struct {
	b []byte
}

func (s *cookieScanner) next(kv *argsKV, decode bool) bool {
	if len(s.b) == 0 {
		return false
	}

	var b []byte
	n := bytes.IndexByte(s.b, ';')
	if n < 0 {
		b = s.b
		s.b = s.b[len(s.b):]
	} else {
		b = s.b[:n]
		s.b = s.b[n+1:]
	}

	n = bytes.IndexByte(b, '=')
	if n < 0 {
		kv.key = kv.key[:0]
		kv.value = decodeCookieArg(kv.value[:0], b, decode)
		return true
	}

	kv.key = decodeCookieArg(kv.key[:0], b[:n], decode)
	kv.value = decodeCookieArg(kv.value[:0], b[n+1:], decode)
	return true
}

func decodeCookieArg(dst, src []byte, decode bool) []byte {
	for len(src) > 0 && src[0] == ' ' {
		src = src[1:]
	}
	for len(src) > 0 && src[len(src)-1] == ' ' {
		src = src[:len(src)-1]
	}
	if !decode {
		return append(dst, src...)
	}
	return decodeArg(dst, src, true)
}
<|endoftext|>


<commit_before>package parser

import (
	"log"
	"regexp"
	"strings"
)

var (
	endRegexp      = regexp.MustCompile(`\s?@end`)
	propertyRegexp = regexp.MustCompile(`@property\s*(?:\((.*)\))?\s*([^\s\*]*)\s*(\*)?\s*(.*);`)
	parentAndProtocolsRegexp = regexp.MustCompile(`@interface[^:<]*(?::\s*([^<\s]*))?(?:\s*<([^>]*)>)?`)
)

var (
	codingInitMethodName   = "initWithCoder:"
	codingInitMethodRegexp = regexp.MustCompile(`\s?-.*` + codingInitMethodName)

	codingEncodeMethodName   = "encodeWithCoder:"
	codingEncodeMedhotRegexp = regexp.MustCompile(`\s?-.*` + codingEncodeMethodName)

	copyingMethodName   = "copyWithZone:"
	copyingMethodRexexp = regexp.MustCompile(`\s?-.*` + copyingMethodName)
)

const (
	propertyRegexpAttrIndex = iota + 1
	propertyRegexpClassIndex
	propertyRegexpPointerIndex
	propertyRegexpNameIndex
)

const (
	parentAndProtocolsRegexpParentIndex = iota + 1
	parentAndProtocolsRegexpProtocolsIndex
)

const (
	nsObjectToken = "NSObject"
)

type ObjCClassFile struct {
	HName, MName string
	Classes      []ObjCClass
}

type ObjCClass struct {
	// These fields are extracted from the header file
	Name              string
	Parent            string
	Protocols         []string
	Properties        []Property // These are also extracted form the implementation file
	ConformsNSCoding  bool
	ConformsNSCopying bool

	// These fields are extracted from the implementation file
	NSCodingInfo struct {
		InitWithCoder   MethodInfo
		EncodeWithCoder MethodInfo
	}
	NSCopyingInfo struct {
		CopyWithZone MethodInfo
	}
}

func (oc *ObjCClass) IsDirectChildOfNSObject() bool {
	return oc.Parent == nsObjectToken
}

type MethodInfo struct {
	Name             string
	PosStart, PosEnd int
}

func NewObjCClass(className string, hInterfaceBytes, mInterfaceBytes, implBytes []byte, implBytesOffset int) ObjCClass {
	propertiesFromH := extractProperties(hInterfaceBytes)
	propertiesFromM := extractProperties(mInterfaceBytes)
	parent, protocols := extractParentAndProtocols(hInterfaceBytes)

	class := ObjCClass{
		Name:       className,
		Parent:     parent,
		Protocols:  protocols,
		Properties: mergeProperties(propertiesFromH, propertiesFromM),
		//TODO: Detect if the class conforms the protocols taking into account the parent protocols too
		ConformsNSCoding:  true,
		ConformsNSCopying: true,
	}

	class.NSCodingInfo.InitWithCoder = extractMethodInfo(codingInitMethodName, className, codingInitMethodRegexp, implBytes, implBytesOffset)
	class.NSCodingInfo.EncodeWithCoder = extractMethodInfo(codingEncodeMethodName, className, codingEncodeMedhotRegexp, implBytes, implBytesOffset)
	class.NSCopyingInfo.CopyWithZone = extractMethodInfo(copyingMethodName, className, copyingMethodRexexp, implBytes, implBytesOffset)
	return class
}

func extractProperties(interfaceBytes []byte) []Property {
	var properties []Property

	matchedProperties := propertyRegexp.FindAllSubmatch(interfaceBytes, -1)
	for _, matchedProperty := range matchedProperties {
		//Split the attributes string and trim each of them
		attributes := strings.Split(string(matchedProperty[propertyRegexpAttrIndex]), ",")
		for i, attr := range attributes {
			attributes[i] = strings.TrimSpace(attr)
		}
		class := string(matchedProperty[propertyRegexpClassIndex])
		pointer := string(matchedProperty[propertyRegexpPointerIndex])
		name := string(matchedProperty[propertyRegexpNameIndex])

		// Add this property to the info
		properties = append(properties, Property{
			Name:       name,
			Class:      class,
			Attributes: attributes,
			IsPointer:  pointer != "",
		})
	}

	return properties
}

func extractParentAndProtocols(interfaceBytes []byte) (parent string, protocols []string) {
	match := parentAndProtocolsRegexp.FindSubmatch(interfaceBytes)
	parent = string(match[parentAndProtocolsRegexpParentIndex])
	protocols = strings.Split(string(match[parentAndProtocolsRegexpProtocolsIndex]), ",")
	for i, proto := range protocols {
		protocols[i] = strings.TrimSpace(proto)
	}
	return
}

func mergeProperties(propertiesFromH, propertiesFromM []Property) []Property {
	// Join the two property slices avoiding duplicates (for example a property in .h with
	// a "readonly" attribute and the same property in .m with "readwrite")
	// Properties in .m have preference.

	hPropsMap := make(map[string]int, len(propertiesFromH))
	for i,prop := range propertiesFromH {
		hPropsMap[prop.Name] = i // Save the index of the property in case we find a duplicate later
	}

	for _,prop := range propertiesFromM {
		if index,exists := hPropsMap[prop.Name]; exists {
			propertiesFromH[index] = prop;
		} else {
			propertiesFromH = append(propertiesFromH,prop)
		}
	}
	return propertiesFromH
}

func extractMethodInfo(methodName, className string, methodSignatureRegexp *regexp.Regexp, implBytes []byte, implBytesOffset int) (methodInfo MethodInfo) {
	matchedMethod := methodSignatureRegexp.FindIndex(implBytes)

	if matchedMethod == nil {
		log.Printf(`Method not found (Regexp: %v) in class "%v"\n`, methodSignatureRegexp, className)
		// There is no previous method, the position for the new one will be just before @end
		matchedEnd := endRegexp.FindIndex(implBytes)
		methodInfo.PosStart, methodInfo.PosEnd = matchedEnd[0], matchedEnd[0]
	} else {
		methodInfo.PosStart = matchedMethod[0]
		bodyStart := matchedMethod[1]
		relativeBodyEnd := relativeEndOfMethodBody(implBytes[bodyStart:])
		methodInfo.PosEnd = bodyStart + relativeBodyEnd
	}

	methodInfo.Name = methodName
	methodInfo.PosStart += implBytesOffset
	methodInfo.PosEnd += implBytesOffset

	return
}

func relativeEndOfMethodBody(bytes []byte) int {
	numBrackets := 0
	insideBody := false

	for i, b := range bytes {
		if b == '{' {
			insideBody = true
			numBrackets++
		} else if b == '}' {
			numBrackets--
		}

		if insideBody && numBrackets <= 0 {
			return i + 1
		}
	}
	return -1
}
<commit_msg>Removed the explicit booleans that told if the class conforms NSCoding and NSCopying protocols<commit_after>package parser

import (
	"log"
	"regexp"
	"strings"
)

var (
	endRegexp      = regexp.MustCompile(`\s?@end`)
	propertyRegexp = regexp.MustCompile(`@property\s*(?:\((.*)\))?\s*([^\s\*]*)\s*(\*)?\s*(.*);`)
	parentAndProtocolsRegexp = regexp.MustCompile(`@interface[^:<]*(?::\s*([^<\s]*))?(?:\s*<([^>]*)>)?`)
)

var (
	codingInitMethodName   = "initWithCoder:"
	codingInitMethodRegexp = regexp.MustCompile(`\s?-.*` + codingInitMethodName)

	codingEncodeMethodName   = "encodeWithCoder:"
	codingEncodeMedhotRegexp = regexp.MustCompile(`\s?-.*` + codingEncodeMethodName)

	copyingMethodName   = "copyWithZone:"
	copyingMethodRexexp = regexp.MustCompile(`\s?-.*` + copyingMethodName)
)

const (
	propertyRegexpAttrIndex = iota + 1
	propertyRegexpClassIndex
	propertyRegexpPointerIndex
	propertyRegexpNameIndex
)

const (
	parentAndProtocolsRegexpParentIndex = iota + 1
	parentAndProtocolsRegexpProtocolsIndex
)

const (
	nsObjectToken = "NSObject"
)

type ObjCClassFile struct {
	HName, MName string
	Classes      []ObjCClass
}

type ObjCClass struct {
	// These fields are extracted from the header file
	Name              string
	Parent            string
	Protocols         []string
	Properties        []Property // These are also extracted form the implementation file

	// These fields are extracted from the implementation file
	NSCodingInfo struct {
		InitWithCoder   MethodInfo
		EncodeWithCoder MethodInfo
	}
	NSCopyingInfo struct {
		CopyWithZone MethodInfo
	}
}

func (oc *ObjCClass) IsDirectChildOfNSObject() bool {
	return oc.Parent == nsObjectToken
}

type MethodInfo struct {
	Name             string
	PosStart, PosEnd int
}

func NewObjCClass(className string, hInterfaceBytes, mInterfaceBytes, implBytes []byte, implBytesOffset int) ObjCClass {
	propertiesFromH := extractProperties(hInterfaceBytes)
	propertiesFromM := extractProperties(mInterfaceBytes)
	parent, protocols := extractParentAndProtocols(hInterfaceBytes)

	class := ObjCClass{
		Name:       className,
		Parent:     parent,
		Protocols:  protocols,
		Properties: mergeProperties(propertiesFromH, propertiesFromM),
	}

	class.NSCodingInfo.InitWithCoder = extractMethodInfo(codingInitMethodName, className, codingInitMethodRegexp, implBytes, implBytesOffset)
	class.NSCodingInfo.EncodeWithCoder = extractMethodInfo(codingEncodeMethodName, className, codingEncodeMedhotRegexp, implBytes, implBytesOffset)
	class.NSCopyingInfo.CopyWithZone = extractMethodInfo(copyingMethodName, className, copyingMethodRexexp, implBytes, implBytesOffset)
	return class
}

func extractProperties(interfaceBytes []byte) []Property {
	var properties []Property

	matchedProperties := propertyRegexp.FindAllSubmatch(interfaceBytes, -1)
	for _, matchedProperty := range matchedProperties {
		//Split the attributes string and trim each of them
		attributes := strings.Split(string(matchedProperty[propertyRegexpAttrIndex]), ",")
		for i, attr := range attributes {
			attributes[i] = strings.TrimSpace(attr)
		}
		class := string(matchedProperty[propertyRegexpClassIndex])
		pointer := string(matchedProperty[propertyRegexpPointerIndex])
		name := string(matchedProperty[propertyRegexpNameIndex])

		// Add this property to the info
		properties = append(properties, Property{
			Name:       name,
			Class:      class,
			Attributes: attributes,
			IsPointer:  pointer != "",
		})
	}

	return properties
}

func extractParentAndProtocols(interfaceBytes []byte) (parent string, protocols []string) {
	match := parentAndProtocolsRegexp.FindSubmatch(interfaceBytes)
	parent = string(match[parentAndProtocolsRegexpParentIndex])
	protocols = strings.Split(string(match[parentAndProtocolsRegexpProtocolsIndex]), ",")
	for i, proto := range protocols {
		protocols[i] = strings.TrimSpace(proto)
	}
	return
}

func mergeProperties(propertiesFromH, propertiesFromM []Property) []Property {
	// Join the two property slices avoiding duplicates (for example a property in .h with
	// a "readonly" attribute and the same property in .m with "readwrite")
	// Properties in .m have preference.

	hPropsMap := make(map[string]int, len(propertiesFromH))
	for i,prop := range propertiesFromH {
		hPropsMap[prop.Name] = i // Save the index of the property in case we find a duplicate later
	}

	for _,prop := range propertiesFromM {
		if index,exists := hPropsMap[prop.Name]; exists {
			propertiesFromH[index] = prop;
		} else {
			propertiesFromH = append(propertiesFromH,prop)
		}
	}
	return propertiesFromH
}

func extractMethodInfo(methodName, className string, methodSignatureRegexp *regexp.Regexp, implBytes []byte, implBytesOffset int) (methodInfo MethodInfo) {
	matchedMethod := methodSignatureRegexp.FindIndex(implBytes)

	if matchedMethod == nil {
		log.Printf(`Method not found (Regexp: %v) in class "%v"\n`, methodSignatureRegexp, className)
		// There is no previous method, the position for the new one will be just before @end
		matchedEnd := endRegexp.FindIndex(implBytes)
		methodInfo.PosStart, methodInfo.PosEnd = matchedEnd[0], matchedEnd[0]
	} else {
		methodInfo.PosStart = matchedMethod[0]
		bodyStart := matchedMethod[1]
		relativeBodyEnd := relativeEndOfMethodBody(implBytes[bodyStart:])
		methodInfo.PosEnd = bodyStart + relativeBodyEnd
	}

	methodInfo.Name = methodName
	methodInfo.PosStart += implBytesOffset
	methodInfo.PosEnd += implBytesOffset

	return
}

func relativeEndOfMethodBody(bytes []byte) int {
	numBrackets := 0
	insideBody := false

	for i, b := range bytes {
		if b == '{' {
			insideBody = true
			numBrackets++
		} else if b == '}' {
			numBrackets--
		}

		if insideBody && numBrackets <= 0 {
			return i + 1
		}
	}
	return -1
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/go-clang/v3.7/clang"
)

const testInterface = `
#pragma once
#include <string>
#include <vector>
namespace one { namespace two { namespace three {
class TestInterface {
virtual ~TestInterface() = default;
virtual void method(int foo, std::string, const std::vector<int>& t) = 0;
};
class TestInterface2 {
virtual ~TestInterface() = default;
virtual void method(int foo, std::string, const std::vector<int>& t) = 0;
};
}}}
`

func TestParserWithSimpleInterface(t *testing.T) {
	var parseOptions uint16 = CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing

	file, err := ioutil.TempFile("", "cpp_codegen_test_parser")
	defer os.Remove(file.Name())
	if err != nil {
		t.Fatalf("Unable to create temporary file")
	}
	defer os.Remove(file.Name())

	_, err4 := file.Write([]byte(testInterface))
	if err4 != nil {
		t.Fatalf("Unable to write to file %s", file.Name())
	}

	err3 := file.Close()
	if err3 != nil {
		t.Fatalf("Unable to close file %s", file.Name())
	}

	// TODO @jupp: this is copy + pasted from main.go, extract this and hide behind an interface
	idx := clang.NewIndex(0, 0)
	defer idx.Dispose()

	tu := idx.ParseTranslationUnit(file.Name(), []string{"-x", "c++"}, nil, parseOptions)
	defer tu.Dispose()

	cursor := tu.TranslationUnitCursor()

	model := NewModel()
	cursor.Visit(func(cursor, parent clang.Cursor) clang.ChildVisitResult {
		return visitAST(cursor, parent, &model)
	})

	fmt.Printf("parsed model %v", model)

	classModel, ok := model.Interfaces["TestInterface"]
	if !ok {
		t.Fatalf("TestInterface not found in model interfaces=%v", model.Interfaces)
	}

	if classModel.Name != "TestInterface" {
		t.Fatalf("class name mismatch, expected TestInterface, got %s", classModel.Name)
	}

	if !reflect.DeepEqual(classModel.Namespaces, []string{"one", "two", "three"}) {
		t.Fatalf("namespaces don't match, expected [one, two, three], got %v", classModel.Namespaces)
	}

	methodModel, ok := classModel.Methods["method"]
	if !ok {
		t.Fatalf("method not found in model")
	}

	if methodModel.Arguments[0].Name != "foo" {
		t.Fatalf("expected foo for argument name 0, got %s", methodModel.Arguments[0].Name)
	}

	if methodModel.Arguments[0].Type != "int" {
		t.Fatalf("wrong type for argument, expected int, got %s", methodModel.Arguments[0].Type)
	}

	if methodModel.Arguments[1].Name != "" {
		t.Fatalf("expected no name for %s argument", methodModel.Arguments[1].Name)
	}

	if methodModel.Arguments[1].Type != "std::string" {
		t.Fatalf("wrong type for argument 1, expected std::string, got %s", methodModel.Arguments[1].Type)
	}

	if methodModel.Arguments[2].Name != "t" {
		t.Fatalf("expected t for argument name 2, got %s", methodModel.Arguments[2].Name)
	}

	if methodModel.Arguments[2].Type != "const std::vector<int> &" {
		t.Fatalf("wrong type for argument2 , expected 'const std::vector<int> &', got %s", methodModel.Arguments[2].Type)
	}

	secondClassModel := model.Interfaces["TestInterface2"]
	if !reflect.DeepEqual(secondClassModel.Methods, classModel.Methods) {
		t.Fatalf("method models should be equal: %v == %v", secondClassModel.Methods, classModel.Methods)
	}

	if !reflect.DeepEqual(secondClassModel.Namespaces, classModel.Namespaces) {
		t.Fatalf("namespace models should be equal: %v == %v", secondClassModel.Namespaces, classModel.Namespaces)
	}
}
<commit_msg>Extract test model generation<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"reflect"
	"testing"

	"github.com/go-clang/v3.7/clang"
)

const testInterface = `
#pragma once
#include <string>
#include <vector>
namespace one { namespace two { namespace three {
class TestInterface {
virtual ~TestInterface() = default;
virtual void method(int foo, std::string, const std::vector<int>& t) = 0;
};
class TestInterface2 {
virtual ~TestInterface() = default;
virtual void method(int foo, std::string, const std::vector<int>& t) = 0;
};
}}}
`

func TestParserWithSimpleInterface(t *testing.T) {
	model := createTestModel(t)

	classModel, ok := model.Interfaces["TestInterface"]
	if !ok {
		t.Fatalf("TestInterface not found in model interfaces=%v", model.Interfaces)
	}

	if classModel.Name != "TestInterface" {
		t.Fatalf("class name mismatch, expected TestInterface, got %s", classModel.Name)
	}

	if !reflect.DeepEqual(classModel.Namespaces, []string{"one", "two", "three"}) {
		t.Fatalf("namespaces don't match, expected [one, two, three], got %v", classModel.Namespaces)
	}

	methodModel, ok := classModel.Methods["method"]
	if !ok {
		t.Fatalf("method not found in model")
	}

	if methodModel.Arguments[0].Name != "foo" {
		t.Fatalf("expected foo for argument name 0, got %s", methodModel.Arguments[0].Name)
	}

	if methodModel.Arguments[0].Type != "int" {
		t.Fatalf("wrong type for argument, expected int, got %s", methodModel.Arguments[0].Type)
	}

	if methodModel.Arguments[1].Name != "" {
		t.Fatalf("expected no name for %s argument", methodModel.Arguments[1].Name)
	}

	if methodModel.Arguments[1].Type != "std::string" {
		t.Fatalf("wrong type for argument 1, expected std::string, got %s", methodModel.Arguments[1].Type)
	}

	if methodModel.Arguments[2].Name != "t" {
		t.Fatalf("expected t for argument name 2, got %s", methodModel.Arguments[2].Name)
	}

	if methodModel.Arguments[2].Type != "const std::vector<int> &" {
		t.Fatalf("wrong type for argument2 , expected 'const std::vector<int> &', got %s", methodModel.Arguments[2].Type)
	}

	secondClassModel := model.Interfaces["TestInterface2"]
	if !reflect.DeepEqual(secondClassModel.Methods, classModel.Methods) {
		t.Fatalf("method models should be equal: %v == %v", secondClassModel.Methods, classModel.Methods)
	}

	if !reflect.DeepEqual(secondClassModel.Namespaces, classModel.Namespaces) {
		t.Fatalf("namespace models should be equal: %v == %v", secondClassModel.Namespaces, classModel.Namespaces)
	}
}

func createTestModel(t *testing.T) Model {
	var parseOptions uint16 = CXTranslationUnit_Incomplete | CXTranslationUnit_SkipFunctionBodies | CXTranslationUnit_KeepGoing

	file, err := ioutil.TempFile("", "cpp_codegen_test_parser")
	defer os.Remove(file.Name())
	if err != nil {
		t.Fatalf("Unable to create temporary file")
	}
	defer os.Remove(file.Name())

	_, err4 := file.Write([]byte(testInterface))
	if err4 != nil {
		t.Fatalf("Unable to write to file %s", file.Name())
	}

	err3 := file.Close()
	if err3 != nil {
		t.Fatalf("Unable to close file %s", file.Name())
	}

	// TODO @jupp: this is copy + pasted from main.go, extract this and hide behind an interface
	idx := clang.NewIndex(0, 0)
	defer idx.Dispose()

	tu := idx.ParseTranslationUnit(file.Name(), []string{"-x", "c++"}, nil, parseOptions)
	defer tu.Dispose()

	cursor := tu.TranslationUnitCursor()

	model := NewModel()
	cursor.Visit(func(cursor, parent clang.Cursor) clang.ChildVisitResult {
		return visitAST(cursor, parent, &model)
	})

	fmt.Printf("parsed model %v", model)

	return model
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go.exp/fsnotify"
	"errors"
	"fmt"
	"gnd.la/log"
	"net"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"sync"
	"text/tabwriter"
)

var services struct {
	sync.Mutex
	list []*Service
}

type servicesByPriority []*Service

func (s servicesByPriority) Len() int           { return len(s) }
func (s servicesByPriority) Less(i, j int) bool { return s[i].Config.Priority < s[j].Config.Priority }
func (s servicesByPriority) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s servicesByPriority) Sort()              { sort.Stable(s) }

type quit struct {
	stop    chan bool
	stopped chan bool
}

func newQuit() *quit {
	return &quit{
		stop:    make(chan bool, 1),
		stopped: make(chan bool, 1),
	}
}

func (q *quit) sendStop() {
	q.stop <- true
}

func (q *quit) sendStopped() {
	q.stopped <- true
}

func ensureUniqueName(cfg *Config) {
	ii := 1
	orig := cfg.ServiceName()
	for {
		name := cfg.ServiceName()
		unique := true
		for _, v := range services.list {
			if name == v.Name() {
				unique = false
				cfg.Name = fmt.Sprintf("%s-%d", orig, ii)
				ii++
				break
			}
		}
		if unique {
			break
		}
	}
}

func startWatching(q *quit) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	go func() {
	End:
		for {
			select {
			case ev := <-watcher.Event:
				name := filepath.Base(ev.Name)
				if shouldIgnoreFile(name) {
					break
				}
				services.Lock()
				switch {
				case ev.IsCreate():
					cfg := ParseConfig(name)
					ensureUniqueName(cfg)
					log.Debugf("added service %s", cfg.ServiceName())
					s := newService(cfg)
					services.list = append(services.list, s)
					servicesByPriority(services.list).Sort()
					s.Start()
				case ev.IsDelete() || ev.IsRename():
					for ii := range services.list {
						s := services.list[ii]
						if s.Config.File == name {
							log.Debugf("removed service %s", s.Name())
							if s.State == StateStarted {
								s.Stop()
							}
							services.list = append(services.list[:ii], services.list[ii+1:]...)
							break
						}
					}
				case ev.IsModify():
					for _, v := range services.list {
						if v.Config.File == name {
							cfg := ParseConfig(name)
							if reflect.DeepEqual(v.Config, cfg) {
								// there were changes to the file which don't affect the conf
								break
							}
							log.Debugf("changed service %s's configuration", v.Name())
							start := false
							if v.State == StateStarted {
								start = v.Stop() == nil
							}
							v.Config = cfg
							servicesByPriority(services.list).Sort()
							if start {
								v.Start()
							}
							break
						}
					}
				default:
					log.Errorf("unhandled event: %s\n", ev)
				}
				services.Unlock()
			case err := <-watcher.Error:
				log.Errorf("error watching: %s", err)
			case <-q.stop:
				watcher.Close()
				q.sendStopped()
				break End
			}
		}
	}()
	if err := watcher.Watch(*configDir); err != nil {
		return err
	}
	return nil
}

func startService(conn net.Conn, s *Service) error {
	name := s.Name()
	encodeResponse(conn, respOk, fmt.Sprintf("starting %s\n", name))
	if serr := s.Start(); serr != nil {
		return encodeResponse(conn, respErr, fmt.Sprintf("error starting %s: %s\n", name, serr))
	}
	return encodeResponse(conn, respOk, fmt.Sprintf("started %s\n", name))
}

func stopService(conn net.Conn, s *Service) (bool, error) {
	name := s.Name()
	encodeResponse(conn, respOk, fmt.Sprintf("stopping %s\n", name))
	if serr := s.Stop(); serr != nil {
		return false, encodeResponse(conn, respErr, fmt.Sprintf("error stopping %s: %s\n", name, serr))
	}
	return true, encodeResponse(conn, respOk, fmt.Sprintf("stopped %s\n", name))
}

func serveConn(conn net.Conn) error {
	defer conn.Close()
	args, err := decodeArgs(conn)
	if err != nil {
		return fmt.Errorf("error decoding arguments: %s", err)
	}
	if len(args) > 0 {
		var err error
		var st *Service
		var name string
		cmd := strings.ToLower(args[0])
		if cmd == "start" || cmd == "stop" || cmd == "restart" || cmd == "log" {
			if len(args) != 2 {
				err = encodeResponse(conn, respErr, fmt.Sprintf("command %s requires exactly one argument\n", cmd))
				cmd = ""
			}
			if cmd != "" {
				services.Lock()
				for _, v := range services.list {
					if sn := v.Name(); sn == args[1] {
						st = v
						name = sn
						break
					}
				}
				services.Unlock()
				if st == nil {
					err = encodeResponse(conn, respErr, fmt.Sprintf("no service named %s\n", args[1]))
					cmd = ""
				}
			}
		}
		switch cmd {
		case "":
			// cmd already handled
		case "start":
			if st.State == StateStarted {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is already running\n", name))
			} else {
				err = startService(conn, st)
			}
		case "stop":
			if st.State != StateStarted {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is not running\n", name))
			} else {
				_, err = stopService(conn, st)
			}
		case "restart":
			var stopped bool
			if st.State == StateStarted {
				stopped, err = stopService(conn, st)
			}
			if stopped {
				err = startService(conn, st)
			}
		case "list":
			var buf bytes.Buffer
			w := tabwriter.NewWriter(&buf, 4, 4, 4, ' ', 0)
			fmt.Fprint(w, "SERVICE\tSTATUS\t\n")
			services.Lock()
			for _, v := range services.list {
				fmt.Fprintf(w, "%s\t", v.Name())
				switch v.State {
				case StateStopped:
					fmt.Fprint(w, "STOPPED")
				case StateStopping:
					fmt.Fprint(w, "STOPPING")
				case StateStarting:
					fmt.Fprint(w, "STARTING")
				case StateStarted:
					if v.Restarts > 0 {
						fmt.Fprintf(w, "RUNNING since %s - %d restarts", formatTime(v.Started), v.Restarts)
					} else {
						fmt.Fprintf(w, "RUNNING since %s", formatTime(v.Started))
					}
				case StateFailed:
					fmt.Fprintf(w, "FAILED - %s", v.Err)
				default:
					panic("invalid state")
				}
				fmt.Fprint(w, "\t\n")
			}
			services.Unlock()
			w.Flush()
			buf.WriteString("\n")
			err = encodeResponse(conn, respOk, buf.String())
		case "log":
			if st.State != StateStarted {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is not running\n", name))
				break
			}
			if st.logger.monitor != nil {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is already being monitored\n", name))
				break
			}
			ch := make(chan bool, 1)
			st.logger.monitor = func(prefix string, b []byte) {
				var buf bytes.Buffer
				buf.WriteByte('[')
				buf.WriteString(prefix)
				buf.WriteString("] ")
				buf.Write(b)
				if b[len(b)-1] != '\n' {
					buf.Write(newLine)
				}
				encodeResponse(conn, respOk, buf.String())
			}
			go func() {
				// log stops when the client sends something over the connection
				// or the connection is closed
				b := make([]byte, 1)
				conn.Read(b)
				conn.Close()
				ch <- true
			}()
			<-ch
			st.logger.monitor = nil
			return nil
		default:
			err = encodeResponse(conn, respErr, fmt.Sprintf("unknown command %s - %s\n", cmd, help))
			if err != nil {
				return err
			}
		}
	}
	return encodeResponse(conn, respEnd, "")
}

func startServer(q *quit) error {
	os.Remove(SocketPath)
	server, err := net.Listen("unix", SocketPath)
	if err != nil {
		return err
	}
	if gid := getGroupId(AppName); gid >= 0 {
		os.Chown(SocketPath, 0, gid)
		os.Chmod(SocketPath, 0775)
	}
	conns := make(chan net.Conn, 10)
	go func() {
		for {
			conn, err := server.Accept()
			if err != nil {
				log.Errorf("error accepting connection: %s", err)
			}
			conns <- conn
		}
	}()
	go func() {
		for {
			select {
			case <-q.stop:
				os.Remove(SocketPath)
				q.sendStopped()
				return
			case conn := <-conns:
				go func() {
					if err := serveConn(conn); err != nil {
						log.Errorf("error serving connection: %s", err)
					}
				}()
			}
		}
	}()
	return nil
}

func daemonMain() error {
	u, err := user.Current()
	if err != nil {
		return err
	}
	if u.Uid != "0" {
		return errors.New("govenator daemon must be run as root")
	}
	// Make logs directory
	os.Mkdir(LogDir, 0755)
	configs, err := ParseConfigs()
	if err != nil {
		return err
	}
	services.Lock()
	services.list = make([]*Service, len(configs))
	for ii, v := range configs {
		ensureUniqueName(v)
		s := newService(v)
		services.list[ii] = s
		s.Start()
	}
	servicesByPriority(services.list).Sort()
	services.Unlock()
	quitWatcher := newQuit()
	if err := startWatching(quitWatcher); err != nil {
		log.Errorf("error watching %s, configuration won't be automatically updated: %s", *configDir, err)
	}
	quitServer := newQuit()
	if err := startServer(quitServer); err != nil {
		log.Errorf("error starting server, can't receive remote commands: %s", err)
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	// Wait for signal
	<-c
	quitWatcher.sendStop()
	quitServer.sendStop()
	// Wait for goroutines to exit cleanly
	<-quitWatcher.stopped
	<-quitServer.stopped
	services.Lock()
	var wg sync.WaitGroup
	wg.Add(len(services.list))
	// Stop in reverse order, to respect priorities
	for ii := len(services.list) - 1; ii >= 0; ii-- {
		v := services.list[ii]
		go func(s *Service) {
			s.Stop()
			wg.Done()
		}(v)
	}
	wg.Wait()
	services.Unlock()
	log.Debugf("daemon exiting")
	return nil
}
<commit_msg>Fix panic on startup, while checking name uniqueness<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go.exp/fsnotify"
	"errors"
	"fmt"
	"gnd.la/log"
	"net"
	"os"
	"os/signal"
	"os/user"
	"path/filepath"
	"reflect"
	"sort"
	"strings"
	"sync"
	"text/tabwriter"
)

var services struct {
	sync.Mutex
	list []*Service
}

type servicesByPriority []*Service

func (s servicesByPriority) Len() int           { return len(s) }
func (s servicesByPriority) Less(i, j int) bool { return s[i].Config.Priority < s[j].Config.Priority }
func (s servicesByPriority) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s servicesByPriority) Sort()              { sort.Stable(s) }

type quit struct {
	stop    chan bool
	stopped chan bool
}

func newQuit() *quit {
	return &quit{
		stop:    make(chan bool, 1),
		stopped: make(chan bool, 1),
	}
}

func (q *quit) sendStop() {
	q.stop <- true
}

func (q *quit) sendStopped() {
	q.stopped <- true
}

func ensureUniqueName(cfg *Config) {
	ii := 1
	orig := cfg.ServiceName()
	for {
		name := cfg.ServiceName()
		unique := true
		for _, v := range services.list {
			if name == v.Name() {
				unique = false
				cfg.Name = fmt.Sprintf("%s-%d", orig, ii)
				ii++
				break
			}
		}
		if unique {
			break
		}
	}
}

func startWatching(q *quit) error {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	go func() {
	End:
		for {
			select {
			case ev := <-watcher.Event:
				name := filepath.Base(ev.Name)
				if shouldIgnoreFile(name) {
					break
				}
				services.Lock()
				switch {
				case ev.IsCreate():
					cfg := ParseConfig(name)
					ensureUniqueName(cfg)
					log.Debugf("added service %s", cfg.ServiceName())
					s := newService(cfg)
					services.list = append(services.list, s)
					servicesByPriority(services.list).Sort()
					s.Start()
				case ev.IsDelete() || ev.IsRename():
					for ii := range services.list {
						s := services.list[ii]
						if s.Config.File == name {
							log.Debugf("removed service %s", s.Name())
							if s.State == StateStarted {
								s.Stop()
							}
							services.list = append(services.list[:ii], services.list[ii+1:]...)
							break
						}
					}
				case ev.IsModify():
					for _, v := range services.list {
						if v.Config.File == name {
							cfg := ParseConfig(name)
							if reflect.DeepEqual(v.Config, cfg) {
								// there were changes to the file which don't affect the conf
								break
							}
							log.Debugf("changed service %s's configuration", v.Name())
							start := false
							if v.State == StateStarted {
								start = v.Stop() == nil
							}
							v.Config = cfg
							servicesByPriority(services.list).Sort()
							if start {
								v.Start()
							}
							break
						}
					}
				default:
					log.Errorf("unhandled event: %s\n", ev)
				}
				services.Unlock()
			case err := <-watcher.Error:
				log.Errorf("error watching: %s", err)
			case <-q.stop:
				watcher.Close()
				q.sendStopped()
				break End
			}
		}
	}()
	if err := watcher.Watch(*configDir); err != nil {
		return err
	}
	return nil
}

func startService(conn net.Conn, s *Service) error {
	name := s.Name()
	encodeResponse(conn, respOk, fmt.Sprintf("starting %s\n", name))
	if serr := s.Start(); serr != nil {
		return encodeResponse(conn, respErr, fmt.Sprintf("error starting %s: %s\n", name, serr))
	}
	return encodeResponse(conn, respOk, fmt.Sprintf("started %s\n", name))
}

func stopService(conn net.Conn, s *Service) (bool, error) {
	name := s.Name()
	encodeResponse(conn, respOk, fmt.Sprintf("stopping %s\n", name))
	if serr := s.Stop(); serr != nil {
		return false, encodeResponse(conn, respErr, fmt.Sprintf("error stopping %s: %s\n", name, serr))
	}
	return true, encodeResponse(conn, respOk, fmt.Sprintf("stopped %s\n", name))
}

func serveConn(conn net.Conn) error {
	defer conn.Close()
	args, err := decodeArgs(conn)
	if err != nil {
		return fmt.Errorf("error decoding arguments: %s", err)
	}
	if len(args) > 0 {
		var err error
		var st *Service
		var name string
		cmd := strings.ToLower(args[0])
		if cmd == "start" || cmd == "stop" || cmd == "restart" || cmd == "log" {
			if len(args) != 2 {
				err = encodeResponse(conn, respErr, fmt.Sprintf("command %s requires exactly one argument\n", cmd))
				cmd = ""
			}
			if cmd != "" {
				services.Lock()
				for _, v := range services.list {
					if sn := v.Name(); sn == args[1] {
						st = v
						name = sn
						break
					}
				}
				services.Unlock()
				if st == nil {
					err = encodeResponse(conn, respErr, fmt.Sprintf("no service named %s\n", args[1]))
					cmd = ""
				}
			}
		}
		switch cmd {
		case "":
			// cmd already handled
		case "start":
			if st.State == StateStarted {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is already running\n", name))
			} else {
				err = startService(conn, st)
			}
		case "stop":
			if st.State != StateStarted {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is not running\n", name))
			} else {
				_, err = stopService(conn, st)
			}
		case "restart":
			var stopped bool
			if st.State == StateStarted {
				stopped, err = stopService(conn, st)
			}
			if stopped {
				err = startService(conn, st)
			}
		case "list":
			var buf bytes.Buffer
			w := tabwriter.NewWriter(&buf, 4, 4, 4, ' ', 0)
			fmt.Fprint(w, "SERVICE\tSTATUS\t\n")
			services.Lock()
			for _, v := range services.list {
				fmt.Fprintf(w, "%s\t", v.Name())
				switch v.State {
				case StateStopped:
					fmt.Fprint(w, "STOPPED")
				case StateStopping:
					fmt.Fprint(w, "STOPPING")
				case StateStarting:
					fmt.Fprint(w, "STARTING")
				case StateStarted:
					if v.Restarts > 0 {
						fmt.Fprintf(w, "RUNNING since %s - %d restarts", formatTime(v.Started), v.Restarts)
					} else {
						fmt.Fprintf(w, "RUNNING since %s", formatTime(v.Started))
					}
				case StateFailed:
					fmt.Fprintf(w, "FAILED - %s", v.Err)
				default:
					panic("invalid state")
				}
				fmt.Fprint(w, "\t\n")
			}
			services.Unlock()
			w.Flush()
			buf.WriteString("\n")
			err = encodeResponse(conn, respOk, buf.String())
		case "log":
			if st.State != StateStarted {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is not running\n", name))
				break
			}
			if st.logger.monitor != nil {
				err = encodeResponse(conn, respErr, fmt.Sprintf("%s is already being monitored\n", name))
				break
			}
			ch := make(chan bool, 1)
			st.logger.monitor = func(prefix string, b []byte) {
				var buf bytes.Buffer
				buf.WriteByte('[')
				buf.WriteString(prefix)
				buf.WriteString("] ")
				buf.Write(b)
				if b[len(b)-1] != '\n' {
					buf.Write(newLine)
				}
				encodeResponse(conn, respOk, buf.String())
			}
			go func() {
				// log stops when the client sends something over the connection
				// or the connection is closed
				b := make([]byte, 1)
				conn.Read(b)
				conn.Close()
				ch <- true
			}()
			<-ch
			st.logger.monitor = nil
			return nil
		default:
			err = encodeResponse(conn, respErr, fmt.Sprintf("unknown command %s - %s\n", cmd, help))
			if err != nil {
				return err
			}
		}
	}
	return encodeResponse(conn, respEnd, "")
}

func startServer(q *quit) error {
	os.Remove(SocketPath)
	server, err := net.Listen("unix", SocketPath)
	if err != nil {
		return err
	}
	if gid := getGroupId(AppName); gid >= 0 {
		os.Chown(SocketPath, 0, gid)
		os.Chmod(SocketPath, 0775)
	}
	conns := make(chan net.Conn, 10)
	go func() {
		for {
			conn, err := server.Accept()
			if err != nil {
				log.Errorf("error accepting connection: %s", err)
			}
			conns <- conn
		}
	}()
	go func() {
		for {
			select {
			case <-q.stop:
				os.Remove(SocketPath)
				q.sendStopped()
				return
			case conn := <-conns:
				go func() {
					if err := serveConn(conn); err != nil {
						log.Errorf("error serving connection: %s", err)
					}
				}()
			}
		}
	}()
	return nil
}

func daemonMain() error {
	u, err := user.Current()
	if err != nil {
		return err
	}
	if u.Uid != "0" {
		return errors.New("govenator daemon must be run as root")
	}
	// Make logs directory
	os.Mkdir(LogDir, 0755)
	configs, err := ParseConfigs()
	if err != nil {
		return err
	}
	services.Lock()
	services.list = make([]*Service, 0, len(configs))
	for _, v := range configs {
		ensureUniqueName(v)
		s := newService(v)
		services.list = append(services.list, s)
		s.Start()
	}
	servicesByPriority(services.list).Sort()
	services.Unlock()
	quitWatcher := newQuit()
	if err := startWatching(quitWatcher); err != nil {
		log.Errorf("error watching %s, configuration won't be automatically updated: %s", *configDir, err)
	}
	quitServer := newQuit()
	if err := startServer(quitServer); err != nil {
		log.Errorf("error starting server, can't receive remote commands: %s", err)
	}
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)
	// Wait for signal
	<-c
	quitWatcher.sendStop()
	quitServer.sendStop()
	// Wait for goroutines to exit cleanly
	<-quitWatcher.stopped
	<-quitServer.stopped
	services.Lock()
	var wg sync.WaitGroup
	wg.Add(len(services.list))
	// Stop in reverse order, to respect priorities
	for ii := len(services.list) - 1; ii >= 0; ii-- {
		v := services.list[ii]
		go func(s *Service) {
			s.Stop()
			wg.Done()
		}(v)
	}
	wg.Wait()
	services.Unlock()
	log.Debugf("daemon exiting")
	return nil
}
<|endoftext|>


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

package controller

import (
	"reflect"
	"testing"

	"k8s.io/apimachinery/pkg/util/sets"
)

func TestAddService(t *testing.T) {
	testCases := []struct {
		expectedValue map[string]sets.String
		initialValue  map[string]sets.String
		serviceToAdd  string
	}{
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString(),
				"svc2": sets.NewString("acct1"),
			},
			initialValue: map[string]sets.String{
				"svc2": sets.NewString("acct1"),
			},
			serviceToAdd: "svc1",
		},
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			serviceToAdd: "svc1",
		},
	}

	for i, c := range testCases {
		m := NewSecureNamingMapping()
		m.mapping = c.initialValue
		m.AddService(c.serviceToAdd)

		if !reflect.DeepEqual(c.expectedValue, m.mapping) {
			t.Errorf("Case %d failed, actual mapping is %v but expected mapping is %v", i, m.mapping, c.expectedValue)
		}
	}
}

func TestRemoveService(t *testing.T) {
	testCases := []struct {
		expectedValue   map[string]sets.String
		initialValue    map[string]sets.String
		serviceToRemove string
	}{
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString(),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString(),
				"svc2": sets.NewString("acct1"),
			},
			serviceToRemove: "svc2",
		},
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			serviceToRemove: "svc2",
		},
	}

	for i, c := range testCases {
		m := NewSecureNamingMapping()
		m.mapping = c.initialValue
		m.RemoveService(c.serviceToRemove)

		if !reflect.DeepEqual(c.expectedValue, m.mapping) {
			t.Errorf("Case %d failed, actual mapping is %v but expected mapping is %v", i, m.mapping, c.expectedValue)
		}
	}
}

func TestSetServiceAccounts(t *testing.T) {
	cases := []struct {
		expectedValue map[string]sets.String
		initialValue  map[string]sets.String
		serviceName   string
		accounts      sets.String
	}{
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1", "acct2"),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString("acct3"),
			},
			serviceName: "svc1",
			accounts:    sets.NewString("acct1", "acct2"),
		},
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1", "acct2"),
				"svc2": sets.NewString("acct3"),
			},
			initialValue: map[string]sets.String{
				"svc2": sets.NewString("acct3"),
			},
			serviceName: "svc1",
			accounts:    sets.NewString("acct1", "acct2"),
		},
	}

	for i, c := range cases {
		m := NewSecureNamingMapping()
		m.mapping = c.initialValue

		m.SetServiceAccounts(c.serviceName, c.accounts)

		if !reflect.DeepEqual(c.expectedValue, m.mapping) {
			t.Errorf("Case %d failed, actual mapping is %v but expected mapping is %v", i, m.mapping, c.expectedValue)
		}
	}
}
<commit_msg>Correct copyright header (#32)<commit_after>// Copyright 2017 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
	"reflect"
	"testing"

	"k8s.io/apimachinery/pkg/util/sets"
)

func TestAddService(t *testing.T) {
	testCases := []struct {
		expectedValue map[string]sets.String
		initialValue  map[string]sets.String
		serviceToAdd  string
	}{
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString(),
				"svc2": sets.NewString("acct1"),
			},
			initialValue: map[string]sets.String{
				"svc2": sets.NewString("acct1"),
			},
			serviceToAdd: "svc1",
		},
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			serviceToAdd: "svc1",
		},
	}

	for i, c := range testCases {
		m := NewSecureNamingMapping()
		m.mapping = c.initialValue
		m.AddService(c.serviceToAdd)

		if !reflect.DeepEqual(c.expectedValue, m.mapping) {
			t.Errorf("Case %d failed, actual mapping is %v but expected mapping is %v", i, m.mapping, c.expectedValue)
		}
	}
}

func TestRemoveService(t *testing.T) {
	testCases := []struct {
		expectedValue   map[string]sets.String
		initialValue    map[string]sets.String
		serviceToRemove string
	}{
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString(),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString(),
				"svc2": sets.NewString("acct1"),
			},
			serviceToRemove: "svc2",
		},
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString("acct1"),
			},
			serviceToRemove: "svc2",
		},
	}

	for i, c := range testCases {
		m := NewSecureNamingMapping()
		m.mapping = c.initialValue
		m.RemoveService(c.serviceToRemove)

		if !reflect.DeepEqual(c.expectedValue, m.mapping) {
			t.Errorf("Case %d failed, actual mapping is %v but expected mapping is %v", i, m.mapping, c.expectedValue)
		}
	}
}

func TestSetServiceAccounts(t *testing.T) {
	cases := []struct {
		expectedValue map[string]sets.String
		initialValue  map[string]sets.String
		serviceName   string
		accounts      sets.String
	}{
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1", "acct2"),
			},
			initialValue: map[string]sets.String{
				"svc1": sets.NewString("acct3"),
			},
			serviceName: "svc1",
			accounts:    sets.NewString("acct1", "acct2"),
		},
		{
			expectedValue: map[string]sets.String{
				"svc1": sets.NewString("acct1", "acct2"),
				"svc2": sets.NewString("acct3"),
			},
			initialValue: map[string]sets.String{
				"svc2": sets.NewString("acct3"),
			},
			serviceName: "svc1",
			accounts:    sets.NewString("acct1", "acct2"),
		},
	}

	for i, c := range cases {
		m := NewSecureNamingMapping()
		m.mapping = c.initialValue

		m.SetServiceAccounts(c.serviceName, c.accounts)

		if !reflect.DeepEqual(c.expectedValue, m.mapping) {
			t.Errorf("Case %d failed, actual mapping is %v but expected mapping is %v", i, m.mapping, c.expectedValue)
		}
	}
}
<|endoftext|>


<commit_before>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/redis"
	"github.com/eirka/eirka-libs/user"
)

func init() {
	user.Secret = "secret"

	// Set up fake Redis connection
	redis.NewRedisMock()
}

func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performJsonRequest(r http.Handler, method, path string, body []byte) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func errorMessage(err error) string {
	return fmt.Sprintf(`{"error_message":"%s"}`, err)
}

func successMessage(message string) string {
	return fmt.Sprintf(`{"success_message":"%s"}`, message)
}

func TestAddTagController(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	mock.ExpectExec("INSERT into tagmap").
		WithArgs(1, 1).
		WillReturnResult(sqlmock.NewResult(1, 1))

	redis.RedisCache.Mock.Command("DEL", "tags:1", "tag:1:1", "image:1")

	request := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), successMessage(audit.AuditAddTag), "HTTP response should match")

}

func TestAddTagControllerNoInput(t *testing.T) {

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	first := performRequest(router, "POST", "/tag/add")

	assert.Equal(t, first.Code, 400, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), errorMessage(e.ErrInvalidParam), "HTTP response should match")

}

func TestAddTagControllerBadInput(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	request := []byte(`{"ib": 0, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 400, "HTTP request code should match")

}

func TestAddTagControllerDuplicate(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	request := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 400, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), errorMessage(e.ErrDuplicateTag), "HTTP response should match")
}

func TestAddTagControllerNoImage(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	request := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 400, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), errorMessage(e.ErrNotFound), "HTTP response should match")
}
<commit_msg>add controller tests<commit_after>package controllers

import (
	"bytes"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/eirka/eirka-libs/audit"
	"github.com/eirka/eirka-libs/db"
	e "github.com/eirka/eirka-libs/errors"
	"github.com/eirka/eirka-libs/redis"
	"github.com/eirka/eirka-libs/user"
)

func init() {
	user.Secret = "secret"

	// Set up fake Redis connection
	redis.NewRedisMock()
}

func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, nil)
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func performJsonRequest(r http.Handler, method, path string, body []byte) *httptest.ResponseRecorder {
	req, _ := http.NewRequest(method, path, bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	r.ServeHTTP(w, req)
	return w
}

func errorMessage(err error) string {
	return fmt.Sprintf(`{"error_message":"%s"}`, err)
}

func successMessage(message string) string {
	return fmt.Sprintf(`{"success_message":"%s"}`, message)
}

func TestAddTagController(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	mock.ExpectExec("INSERT into tagmap").
		WithArgs(1, 1).
		WillReturnResult(sqlmock.NewResult(1, 1))

	redis.RedisCache.Mock.Command("DEL", "tags:1", "tag:1:1", "image:1")

	request := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 200, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), successMessage(audit.AuditAddTag), "HTTP response should match")

}

func TestAddTagControllerNoInput(t *testing.T) {

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	first := performRequest(router, "POST", "/tag/add")

	assert.Equal(t, first.Code, 400, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), errorMessage(e.ErrInvalidParam), "HTTP response should match")

}

func TestAddTagControllerBadInput(t *testing.T) {

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	request := []byte(`{"ib": 0, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 400, "HTTP request code should match")

}

func TestAddTagControllerDuplicate(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	duperows := sqlmock.NewRows([]string{"count"}).AddRow(1)
	mock.ExpectQuery(`select count\(1\) from tagmap`).WillReturnRows(duperows)

	request := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 400, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), errorMessage(e.ErrDuplicateTag), "HTTP response should match")
}

func TestAddTagControllerNoImage(t *testing.T) {

	var err error

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(user.Auth(false))

	router.POST("/tag/add", AddTagController)

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	statusrows := sqlmock.NewRows([]string{"count"}).AddRow(0)
	mock.ExpectQuery(`SELECT count\(1\) FROM images`).WillReturnRows(statusrows)

	request := []byte(`{"ib": 1, "tag": 1, "image": 1}`)

	first := performJsonRequest(router, "POST", "/tag/add", request)

	assert.Equal(t, first.Code, 400, "HTTP request code should match")
	assert.JSONEq(t, first.Body.String(), errorMessage(e.ErrNotFound), "HTTP response should match")
}
<|endoftext|>


<commit_before>// Copyright 2014 Damjan Cvetko. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package pcapgo

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"time"

	"bufio"
	"compress/gzip"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
)

// Reader wraps an underlying io.Reader to read packet data in PCAP
// format.  See http://wiki.wireshark.org/Development/LibpcapFileFormat
// for information on the file format.
//
// We currenty read v2.4 file format with nanosecond and microsecdond
// timestamp resolution in little-endian and big-endian encoding.
//
// If the PCAP data is gzip compressed it is transparently uncompressed
// by wrapping the given io.Reader with a gzip.Reader.
type Reader struct {
	r              io.Reader
	byteOrder      binary.ByteOrder
	nanoSecsFactor uint32
	versionMajor   uint16
	versionMinor   uint16
	// timezone
	// sigfigs
	snaplen  uint32
	linkType layers.LinkType
	// reusable buffer
	buf [16]byte
}

const magicNanoseconds = 0xA1B23C4D
const magicMicrosecondsBigendian = 0xD4C3B2A1
const magicNanosecondsBigendian = 0x4D3CB2A1

const magicGzip1 = 0x1f
const magicGzip2 = 0x8b

// NewReader returns a new reader object, for reading packet data from
// the given reader. The reader must be open and header data is
// read from it at this point.
// If the file format is not supported an error is returned
//
//  // Create new reader:
//  f, _ := os.Open("/tmp/file.pcap")
//  defer f.Close()
//  r, err := NewReader(f)
//  data, ci, err := r.ReadPacketData()
func NewReader(r io.Reader) (*Reader, error) {
	ret := Reader{r: r}
	if err := ret.readHeader(); err != nil {
		return nil, err
	}
	return &ret, nil
}

func (r *Reader) readHeader() error {
	br := bufio.NewReader(r.r)
	gzipMagic, err := br.Peek(2)
	if err != nil {
		return err
	}

	if gzipMagic[0] == magicGzip1 && gzipMagic[1] == magicGzip2 {
		if r.r, err = gzip.NewReader(br); err != nil {
			return err
		}
	} else {
		r.r = br
	}

	buf := make([]byte, 24)
	if n, err := io.ReadFull(r.r, buf); err != nil {
		return err
	} else if n < 24 {
		return errors.New("Not enough data for read")
	}
	if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1
	} else if magic == magicNanosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1
	} else if magic == magicMicroseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1000
	} else if magic == magicMicrosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1000
	} else {
		return errors.New(fmt.Sprintf("Unknown magic %x", magic))
	}
	if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor {
		return errors.New(fmt.Sprintf("Unknown major version %d", r.versionMajor))
	}
	if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor {
		return errors.New(fmt.Sprintf("Unknown minor version %d", r.versionMinor))
	}
	// ignore timezone 8:12 and sigfigs 12:16
	r.snaplen = r.byteOrder.Uint32(buf[16:20])
	r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24]))
	return nil
}

// Read next packet from file
func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
	if ci, err = r.readPacketHeader(); err != nil {
		return
	}
	if ci.CaptureLength > int(r.snaplen) {
		err = fmt.Errorf("capture length exceeds snap length: %d > %d", 16+ci.CaptureLength, len(r.snaplen))
		return
	}
	data = make([]byte, ci.CaptureLength)
	_, err = io.ReadFull(r.r, data)
	return data, ci, err
}

func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
	if _, err = io.ReadFull(r.r, r.buf[:]); err != nil {
		return
	}
	ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC()
	ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12]))
	ci.Length = int(r.byteOrder.Uint32(r.buf[12:16]))
	return
}

// LinkType returns network, as a layers.LinkType.
func (r *Reader) LinkType() layers.LinkType {
	return r.linkType
}

// Reader formater
func (r *Reader) String() string {
	return fmt.Sprintf("PcapFile  maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType)
}
<commit_msg>Fix erroneous len() of int.<commit_after>// Copyright 2014 Damjan Cvetko. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package pcapgo

import (
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"time"

	"bufio"
	"compress/gzip"
	"github.com/google/gopacket"
	"github.com/google/gopacket/layers"
)

// Reader wraps an underlying io.Reader to read packet data in PCAP
// format.  See http://wiki.wireshark.org/Development/LibpcapFileFormat
// for information on the file format.
//
// We currenty read v2.4 file format with nanosecond and microsecdond
// timestamp resolution in little-endian and big-endian encoding.
//
// If the PCAP data is gzip compressed it is transparently uncompressed
// by wrapping the given io.Reader with a gzip.Reader.
type Reader struct {
	r              io.Reader
	byteOrder      binary.ByteOrder
	nanoSecsFactor uint32
	versionMajor   uint16
	versionMinor   uint16
	// timezone
	// sigfigs
	snaplen  uint32
	linkType layers.LinkType
	// reusable buffer
	buf [16]byte
}

const magicNanoseconds = 0xA1B23C4D
const magicMicrosecondsBigendian = 0xD4C3B2A1
const magicNanosecondsBigendian = 0x4D3CB2A1

const magicGzip1 = 0x1f
const magicGzip2 = 0x8b

// NewReader returns a new reader object, for reading packet data from
// the given reader. The reader must be open and header data is
// read from it at this point.
// If the file format is not supported an error is returned
//
//  // Create new reader:
//  f, _ := os.Open("/tmp/file.pcap")
//  defer f.Close()
//  r, err := NewReader(f)
//  data, ci, err := r.ReadPacketData()
func NewReader(r io.Reader) (*Reader, error) {
	ret := Reader{r: r}
	if err := ret.readHeader(); err != nil {
		return nil, err
	}
	return &ret, nil
}

func (r *Reader) readHeader() error {
	br := bufio.NewReader(r.r)
	gzipMagic, err := br.Peek(2)
	if err != nil {
		return err
	}

	if gzipMagic[0] == magicGzip1 && gzipMagic[1] == magicGzip2 {
		if r.r, err = gzip.NewReader(br); err != nil {
			return err
		}
	} else {
		r.r = br
	}

	buf := make([]byte, 24)
	if n, err := io.ReadFull(r.r, buf); err != nil {
		return err
	} else if n < 24 {
		return errors.New("Not enough data for read")
	}
	if magic := binary.LittleEndian.Uint32(buf[0:4]); magic == magicNanoseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1
	} else if magic == magicNanosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1
	} else if magic == magicMicroseconds {
		r.byteOrder = binary.LittleEndian
		r.nanoSecsFactor = 1000
	} else if magic == magicMicrosecondsBigendian {
		r.byteOrder = binary.BigEndian
		r.nanoSecsFactor = 1000
	} else {
		return errors.New(fmt.Sprintf("Unknown magic %x", magic))
	}
	if r.versionMajor = r.byteOrder.Uint16(buf[4:6]); r.versionMajor != versionMajor {
		return errors.New(fmt.Sprintf("Unknown major version %d", r.versionMajor))
	}
	if r.versionMinor = r.byteOrder.Uint16(buf[6:8]); r.versionMinor != versionMinor {
		return errors.New(fmt.Sprintf("Unknown minor version %d", r.versionMinor))
	}
	// ignore timezone 8:12 and sigfigs 12:16
	r.snaplen = r.byteOrder.Uint32(buf[16:20])
	r.linkType = layers.LinkType(r.byteOrder.Uint32(buf[20:24]))
	return nil
}

// Read next packet from file
func (r *Reader) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
	if ci, err = r.readPacketHeader(); err != nil {
		return
	}
	if ci.CaptureLength > int(r.snaplen) {
		err = fmt.Errorf("capture length exceeds snap length: %d > %d", 16+ci.CaptureLength, r.snaplen)
		return
	}
	data = make([]byte, ci.CaptureLength)
	_, err = io.ReadFull(r.r, data)
	return data, ci, err
}

func (r *Reader) readPacketHeader() (ci gopacket.CaptureInfo, err error) {
	if _, err = io.ReadFull(r.r, r.buf[:]); err != nil {
		return
	}
	ci.Timestamp = time.Unix(int64(r.byteOrder.Uint32(r.buf[0:4])), int64(r.byteOrder.Uint32(r.buf[4:8])*r.nanoSecsFactor)).UTC()
	ci.CaptureLength = int(r.byteOrder.Uint32(r.buf[8:12]))
	ci.Length = int(r.byteOrder.Uint32(r.buf[12:16]))
	return
}

// LinkType returns network, as a layers.LinkType.
func (r *Reader) LinkType() layers.LinkType {
	return r.linkType
}

// Reader formater
func (r *Reader) String() string {
	return fmt.Sprintf("PcapFile  maj: %x min: %x snaplen: %d linktype: %s", r.versionMajor, r.versionMinor, r.snaplen, r.linkType)
}
<|endoftext|>