<commit_before>// Package imap partially implements the Internet Message Access Protocol as
// defined in RFC 3501. Specifically, AUTHENTICATE, STARTLS, and SEARCH remain
// unimplemented.
//
// Untagged IMAP responses are parsed into a Mailbox struct, which tracks all
// currently known information concerning the state of the remote mailbox.
// Because no significant information is returned through tagged responses,
// interaction with Mailbox is necessary for all queries.

package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	tp "net/textproto"
	"strings"
	"sync"
)

// The IMAP client.
type Client struct {
	// The underlying textproto connection may be used to extend the
	// functionality of this package; however, using Client.Cmd instead is
	// recommended, as doing so better preserves thread-safety.
	Text *tp.Conn

	Box  *Mailbox

	conn net.Conn // underlying raw connection.
	tags map[string]chan string
	tMut *sync.Mutex

	lit  chan string // channel where the literal string to be dumped is stored
}

// Represents the current known state of the remote server.
type Mailbox struct {
	capabilities []string
	mut          *sync.RWMutex
}

func (m *Mailbox) Capable(c string) bool {
	m.mut.RLock()
	for _, ca := range m.capabilities {
		if c == ca {
			return true
		}
	}
	m.mut.RUnlock()
	return false
}

// Dial creates an unsecured connection to the IMAP server at the given address
// and returns the corresponding Client.
func Dial(addr string) (*Client, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	return NewClient(conn)
}

// DialTLS creates a TLS_secured connection to the IMAP server at the given
// address and returns the corresponding Client.
func DialTLS(addr string) (*Client, error) {
	conn, err := tls.Dial("tcp", addr, nil)
	if err != nil {
		return nil, err
	}
	return NewClient(conn)
}

// NewClient returns a new Client using an existing connection.
func NewClient(conn net.Conn) (*Client, error) {
	text := tp.NewConn(conn)
	client := &Client{
		Text: text,
		Box:  &Mailbox{
			capabilities: []string{},
			mut: new(sync.RWMutex),
		},
		conn: conn,
		tags: map[string]chan string{},
		tMut: new(sync.Mutex),
		lit: make(chan string),
	}

	input := make(chan string)

	// Read all input from conn
	go func() {
		l, err := text.ReadLine()
		for err == nil {
			input <- l
			l, err = text.ReadLine()
		}
		if err == io.EOF {
			close(input)
		} else {
			panic(err)
		}
	}()

	// Start the serving goroutine
	go func() {
		for {
			select {
			case l := <-input:
				if l[0] == '+' {
					// server is ready for transmission of literal string
					client.Text.PrintfLine(<-client.lit)
					continue
				} else if isUntagged(l) {
					client.handleUntagged(l[2:])
					continue
				}
				// handle tagged response
				ps := strings.SplitN(l, " ", 2)
				tag := ps[0]
				l = ps[1]
				client.tMut.Lock()
				client.tags[tag] <- l
				client.tMut.Unlock()
			}
		}
	}()
	return client, nil
}

func (c *Client) handleUntagged(l string) {
	c.Box.mut.Lock()
	switch l[0:strings.Index(l, " ")] {
	case "CAPABILITY":
		c.Box.capabilities = strings.Split(l, " ")[1:]
	default:
		println(l)
	}
	c.Box.mut.Unlock()
}

// Sends a command and retreives the tagged response.
func (c *Client) Cmd(format string, args ...interface{}) error {
	c.tMut.Lock()
	t := c.Text
	id := t.Next()
	tag := fmt.Sprintf("x%d", id)
	t.StartRequest(id)
	err := t.PrintfLine("%s %s", tag, fmt.Sprintf(format, args...))
	if err != nil {
		return err
	}
	t.EndRequest(id)

	t.StartResponse(id)
	defer t.EndResponse(id)

	ch := make(chan string)
	c.tags[tag] = ch
	c.tMut.Unlock()

	l := <-ch
	if l[0:2] == "OK" {
		return nil
	}
	return errors.New(l)
}

// Equivalent to Cmd, but the first argument (which will be rotated to be the
// last) is sent as a literal string.
func (c *Client) CmdLit(lit, format string, args ...interface{}) error {
	c.tMut.Lock()
	t := c.Text
	id := t.Next()
	tag := fmt.Sprintf("x%d", id)
	t.StartRequest(id)
	err := t.PrintfLine("%s %s {%d}", tag, fmt.Sprintf(format, args...), len(lit))
	if err != nil {
		return err
	}
	t.EndRequest(id)

	c.lit <- lit

	t.StartResponse(id)
	defer t.EndResponse(id)

	ch := make(chan string)
	c.tags[tag] = ch
	c.tMut.Unlock()

	l := <-ch
	if l[0:2] == "OK" {
		return nil
	}
	return errors.New(l)
}

func isUntagged(l string) bool {
	return l[0] != 'x' // all tags are x00
}

// Noop sends a NOOP command to the server, which may be abused to test that
// the connection is still working, or keep it active.
func (c *Client) Noop() error {
	return c.Cmd("NOOP")
}

// Capability determines the server's capabilities.
func (c *Client) Capability() error {
	return c.Cmd("CAPABILITY")
}

// Login authenticates a client using the provided username and password. This
// method is only secure if TLS is being used. AUTHENTICATE and STARTTLS are
// not supported.
func (c *Client) Login(username, password string) error {
	return c.Cmd("LOGIN %s %s", username, password)
}

// Logout closes the connection, after instructing the server to do the same.
func (c *Client) Logout() error {
	return c.Cmd("LOGOUT")
}

// Select selects the specified IMAP mailbox, updating its information in the
// Mailbox object.
func (c *Client) Select(mb string) error {
	return c.Cmd(`SELECT "%s"`, mb)
}

// Examine is identical to select, but marks the mailbox read-only.
func (c *Client) Examine(mb string) error {
	return c.Cmd(`EXAMINE "%s"`, mb)
}

// Create creates the named mailbox.
func (c *Client) Create(mb string) error {
	return c.Cmd(`CREATE "%s"`, mb)
}

// Delete deletes the named mailbox.
func (c *Client) Delete(mb string) error {
	return c.Cmd(`DELETE "%s"`, mb)
}

// Rename renames the named mailbox to the new name.
func (c *Client) Rename(mb, name string) error {
	return c.Cmd(`RENAME "%s" "%s"`, mb, name)
}

// Subscribe adds the named mailbox to the list of "active" or "subscribed"
// mailboxes, to be used with Lsub .
func (c *Client) Subscribe(mb string) error {
	return c.Cmd(`SUBSCRIBE "%s"`, mb)
}

// Unsubscribe removes the named mailbox from the server's list of "active"
// mailboxes.
func (c *Client) Unsubscribe(mb string) error {
	return c.Cmd(`UNSUBSCRIBE "%s"`, mb)
}

// List lists all folder within basename that match the wildcard expression mb.
// The result is put into the Client's Mailbox struct.
func (c *Client) List(basename, mb string) error {
	return c.Cmd(`LIST "%s" "%s"`, basename, mb)
}

// Lsub is like List, but only operates on "active" mailboxes, as set with
// Subscribe and Unsubscribe.
func (c *Client) Lsub(basename, mb string) error {
	return c.Cmd(`LSUB "%s" "%s"`, basename, mb)
}

// Status queries the specified statuses of the indicated mailbox. This command
// should not be used on the currently selected mailbox. The legal status items
// are:
//
//	MESSAGES	The number of messages in the mailbox.
//	RECENT		The number of messages with the \Recent flag set.
//	UIDNEXT		The next unique identifier value of the mailbox.
//	UIDVALIDITY	The unique identifier validity value of the mailbox.
//	UNSEEN		The number of messages which do not have the \Seen flag set.
//
func (c *Client) Status(mb string, ss ...string) error {
	st := sliceAsString(ss)
	return c.Cmd(`STATUS "%s" %s`, mb, st)
}

// Append appends a message to the specified mailbox, which must exist.
//
// TODO handle flags and the optional date/time string.
func (c *Client) Append(mb, message string) error {
	return c.CmdLit(message, "APPEND \"%s\"", mb)
}

// Check tells the server to perform any necessary housekeeping.
func (c *Client) Check() error {
	return c.Cmd(`CHECK`)
}

// Close closes the selected mailbox, permanently deleting any marked messages
// in the process.
func (c *Client) Close() error {
	return c.Cmd(`CLOSE`)
}

// Expunge permanently removes all marked messages in the selected mailbox.
func (c *Client) Expunge() error {
	return c.Cmd(`EXPUNGE`)
}

// SEARCH remains unimplemented.

// FETCH

// STORE

// COPY

// UID

// Converts a slice of strings to a parenthesized list of space-separated
// strings.
func sliceAsString(ss []string) string {
	return "(" + strings.Join(ss, " ") + ")"
}
<commit_msg>More documentation<commit_after>// Package imap partially implements the Internet Message Access Protocol as
// defined in RFC 3501. Specifically, AUTHENTICATE, STARTLS, and SEARCH remain
// unimplemented.
//
// Untagged IMAP responses are parsed into a Mailbox struct, which tracks all
// currently known information concerning the state of the remote mailbox.
// Because no significant information is returned through tagged responses,
// interaction with Mailbox is necessary for all queries.

package main

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"net"
	tp "net/textproto"
	"strings"
	"sync"
)

// The IMAP client. All methods on the client are thread-safe and executed
// synchronously.
type Client struct {
	// The underlying textproto connection may be used to extend the
	// functionality of this package; however, using Client.Cmd instead is
	// recommended, as doing so better preserves thread-safety.
	Text *tp.Conn

	Box  *Mailbox

	conn net.Conn // underlying raw connection.
	tags map[string]chan string
	tMut *sync.Mutex

	lit  chan string // channel where the literal string to be dumped is stored
}

// Represents the current known state of the remote server.
type Mailbox struct {
	capabilities []string
	mut          *sync.RWMutex
}

func (m *Mailbox) Capable(c string) bool {
	m.mut.RLock()
	for _, ca := range m.capabilities {
		if c == ca {
			return true
		}
	}
	m.mut.RUnlock()
	return false
}

// Dial creates an unsecured connection to the IMAP server at the given address
// and returns the corresponding Client.
func Dial(addr string) (*Client, error) {
	conn, err := net.Dial("tcp", addr)
	if err != nil {
		return nil, err
	}
	return NewClient(conn)
}

// DialTLS creates a TLS_secured connection to the IMAP server at the given
// address and returns the corresponding Client.
func DialTLS(addr string) (*Client, error) {
	conn, err := tls.Dial("tcp", addr, nil)
	if err != nil {
		return nil, err
	}
	return NewClient(conn)
}

// NewClient returns a new Client using an existing connection.
func NewClient(conn net.Conn) (*Client, error) {
	text := tp.NewConn(conn)
	client := &Client{
		Text: text,
		Box:  &Mailbox{
			capabilities: []string{},
			mut: new(sync.RWMutex),
		},
		conn: conn,
		tags: map[string]chan string{},
		tMut: new(sync.Mutex),
		lit: make(chan string),
	}

	input := make(chan string)

	// Read all input from conn
	go func() {
		l, err := text.ReadLine()
		for err == nil {
			input <- l
			l, err = text.ReadLine()
		}
		if err == io.EOF {
			close(input)
		} else {
			panic(err)
		}
	}()

	// Start the serving goroutine
	go func() {
		for {
			select {
			case l := <-input:
				if l[0] == '+' {
					// server is ready for transmission of literal string
					client.Text.PrintfLine(<-client.lit)
					continue
				} else if isUntagged(l) {
					client.handleUntagged(l[2:])
					continue
				}
				// handle tagged response
				ps := strings.SplitN(l, " ", 2)
				tag := ps[0]
				l = ps[1]
				client.tMut.Lock()
				client.tags[tag] <- l
				client.tMut.Unlock()
			}
		}
	}()
	return client, nil
}

func (c *Client) handleUntagged(l string) {
	c.Box.mut.Lock()
	switch l[0:strings.Index(l, " ")] {
	case "CAPABILITY":
		c.Box.capabilities = strings.Split(l, " ")[1:]
	default:
		println(l)
	}
	c.Box.mut.Unlock()
}

// Sends a command and retreives the tagged response.
func (c *Client) Cmd(format string, args ...interface{}) error {
	c.tMut.Lock()
	t := c.Text
	id := t.Next()
	tag := fmt.Sprintf("x%d", id)
	t.StartRequest(id)
	err := t.PrintfLine("%s %s", tag, fmt.Sprintf(format, args...))
	if err != nil {
		return err
	}
	t.EndRequest(id)

	t.StartResponse(id)
	defer t.EndResponse(id)

	ch := make(chan string)
	c.tags[tag] = ch
	c.tMut.Unlock()

	l := <-ch
	if l[0:2] == "OK" {
		return nil
	}
	return errors.New(l)
}

// Equivalent to Cmd, but the first argument (which will be rotated to be the
// last) is sent as a literal string.
func (c *Client) CmdLit(lit, format string, args ...interface{}) error {
	c.tMut.Lock()
	t := c.Text
	id := t.Next()
	tag := fmt.Sprintf("x%d", id)
	t.StartRequest(id)
	err := t.PrintfLine("%s %s {%d}", tag, fmt.Sprintf(format, args...), len(lit))
	if err != nil {
		return err
	}
	t.EndRequest(id)

	c.lit <- lit

	t.StartResponse(id)
	defer t.EndResponse(id)

	ch := make(chan string)
	c.tags[tag] = ch
	c.tMut.Unlock()

	l := <-ch
	if l[0:2] == "OK" {
		return nil
	}
	return errors.New(l)
}

func isUntagged(l string) bool {
	return l[0] != 'x' // all tags are x00
}

// Noop sends a NOOP command to the server, which may be abused to test that
// the connection is still working, or keep it active.
func (c *Client) Noop() error {
	return c.Cmd("NOOP")
}

// Capability determines the server's capabilities.
func (c *Client) Capability() error {
	return c.Cmd("CAPABILITY")
}

// Login authenticates a client using the provided username and password. This
// method is only secure if TLS is being used. AUTHENTICATE and STARTTLS are
// not supported.
func (c *Client) Login(username, password string) error {
	return c.Cmd("LOGIN %s %s", username, password)
}

// Logout closes the connection, after instructing the server to do the same.
func (c *Client) Logout() error {
	return c.Cmd("LOGOUT")
}

// Select selects the specified IMAP mailbox, updating its information in the
// Mailbox object.
func (c *Client) Select(mb string) error {
	return c.Cmd(`SELECT "%s"`, mb)
}

// Examine is identical to select, but marks the mailbox read-only.
func (c *Client) Examine(mb string) error {
	return c.Cmd(`EXAMINE "%s"`, mb)
}

// Create creates the named mailbox.
func (c *Client) Create(mb string) error {
	return c.Cmd(`CREATE "%s"`, mb)
}

// Delete deletes the named mailbox.
func (c *Client) Delete(mb string) error {
	return c.Cmd(`DELETE "%s"`, mb)
}

// Rename renames the named mailbox to the new name.
func (c *Client) Rename(mb, name string) error {
	return c.Cmd(`RENAME "%s" "%s"`, mb, name)
}

// Subscribe adds the named mailbox to the list of "active" or "subscribed"
// mailboxes, to be used with Lsub .
func (c *Client) Subscribe(mb string) error {
	return c.Cmd(`SUBSCRIBE "%s"`, mb)
}

// Unsubscribe removes the named mailbox from the server's list of "active"
// mailboxes.
func (c *Client) Unsubscribe(mb string) error {
	return c.Cmd(`UNSUBSCRIBE "%s"`, mb)
}

// List lists all folder within basename that match the wildcard expression mb.
// The result is put into the Client's Mailbox struct.
func (c *Client) List(basename, mb string) error {
	return c.Cmd(`LIST "%s" "%s"`, basename, mb)
}

// Lsub is like List, but only operates on "active" mailboxes, as set with
// Subscribe and Unsubscribe.
func (c *Client) Lsub(basename, mb string) error {
	return c.Cmd(`LSUB "%s" "%s"`, basename, mb)
}

// Status queries the specified statuses of the indicated mailbox. This command
// should not be used on the currently selected mailbox. The legal status items
// are:
//
//	MESSAGES	The number of messages in the mailbox.
//	RECENT		The number of messages with the \Recent flag set.
//	UIDNEXT		The next unique identifier value of the mailbox.
//	UIDVALIDITY	The unique identifier validity value of the mailbox.
//	UNSEEN		The number of messages which do not have the \Seen flag set.
//
func (c *Client) Status(mb string, ss ...string) error {
	st := sliceAsString(ss)
	return c.Cmd(`STATUS "%s" %s`, mb, st)
}

// Append appends a message to the specified mailbox, which must exist.
//
// TODO handle flags and the optional date/time string.
func (c *Client) Append(mb, message string) error {
	return c.CmdLit(message, "APPEND \"%s\"", mb)
}

// Check tells the server to perform any necessary housekeeping.
func (c *Client) Check() error {
	return c.Cmd(`CHECK`)
}

// Close closes the selected mailbox, permanently deleting any marked messages
// in the process.
func (c *Client) Close() error {
	return c.Cmd(`CLOSE`)
}

// Expunge permanently removes all marked messages in the selected mailbox.
func (c *Client) Expunge() error {
	return c.Cmd(`EXPUNGE`)
}

// SEARCH remains unimplemented.

// FETCH

// STORE

// COPY

// UID

// Converts a slice of strings to a parenthesized list of space-separated
// strings.
func sliceAsString(ss []string) string {
	return "(" + strings.Join(ss, " ") + ")"
}
<|endoftext|>


<commit_before>// Copyright 2010 The Go Authors. All rights reserved.
// Copyright 2011 ThePiachu. All rights reserved.
// Copyright 2013 Conformal Systems LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package btcec

// This package operates, internally, on Jacobian coordinates. For a given
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
// calculation can be performed within the transform (as in ScalarMult and
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
// reverse the transform than to operate in affine coordinates.

import (
	"crypto/elliptic"
	"math/big"
	"sync"
)

//TODO: examine if we need to care about EC optimization as descibed here
//      https://bitcointalk.org/index.php?topic=155054.0;all

// KoblitzCurve supports a koblitz curve implementation that fits the ECC Curve
// interface from crypto/elliptic.
type KoblitzCurve struct {
	*elliptic.CurveParams
	q *big.Int
}

// Params returns the parameters fro the curve.
func (curve *KoblitzCurve) Params() *elliptic.CurveParams {
	return curve.CurveParams
}

// IsOnCurve returns boolean if the point (x,y) is on the curve.
// Part of the elliptic.Curve interface. This function differs from the
// crypto/elliptic algorithm since a = 0 not -3.
func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool {
	// y² = x³ + b
	y2 := new(big.Int).Mul(y, y) //y²
	y2.Mod(y2, curve.P)          //y²%P

	x3 := new(big.Int).Mul(x, x) //x²
	x3.Mul(x3, x)                //x³

	x3.Add(x3, curve.B) //x³+B
	x3.Mod(x3, curve.P) //(x³+B)%P

	return x3.Cmp(y2) == 0
}

// zForAffine returns a Jacobian Z value for the affine point (x, y). If x and
// y are zero, it assumes that they represent the point at infinity because (0,
// 0) is not on the any of the curves handled here.
func zForAffine(x, y *big.Int) *big.Int {
	z := new(big.Int)
	if x.Sign() != 0 || y.Sign() != 0 {
		z.SetInt64(1)
	}
	return z
}

// affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file. If the point is ∞ it returns 0, 0.
func (curve *KoblitzCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
	if z.Sign() == 0 {
		return new(big.Int), new(big.Int)
	}

	zinv := new(big.Int).ModInverse(z, curve.P)
	zinvsq := new(big.Int).Mul(zinv, zinv)

	xOut = new(big.Int).Mul(x, zinvsq)
	xOut.Mod(xOut, curve.P)
	zinvsq.Mul(zinvsq, zinv)
	yOut = new(big.Int).Mul(y, zinvsq)
	yOut.Mod(yOut, curve.P)
	return
}

// Add returns the sum of (x1,y1 and (x2,y2). Part of the elliptic.Curve
// interface.
func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
	z1 := zForAffine(x1, y1)
	z2 := zForAffine(x2, y2)
	return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2))
}

// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form.
func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
	// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
	x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int)
	if z1.Sign() == 0 {
		x3.Set(x2)
		y3.Set(y2)
		z3.Set(z2)
		return x3, y3, z3
	}
	if z2.Sign() == 0 {
		x3.Set(x1)
		y3.Set(y1)
		z3.Set(z1)
		return x3, y3, z3
	}

	z1z1 := new(big.Int).Mul(z1, z1)
	z1z1.Mod(z1z1, curve.P)
	z2z2 := new(big.Int).Mul(z2, z2)
	z2z2.Mod(z2z2, curve.P)

	u1 := new(big.Int).Mul(x1, z2z2)
	u1.Mod(u1, curve.P)
	u2 := new(big.Int).Mul(x2, z1z1)
	u2.Mod(u2, curve.P)
	h := new(big.Int).Sub(u2, u1)
	xEqual := h.Sign() == 0
	if h.Sign() == -1 {
		h.Add(h, curve.P)
	}
	i := new(big.Int).Lsh(h, 1)
	i.Mul(i, i)
	j := new(big.Int).Mul(h, i)

	s1 := new(big.Int).Mul(y1, z2)
	s1.Mul(s1, z2z2)
	s1.Mod(s1, curve.P)
	s2 := new(big.Int).Mul(y2, z1)
	s2.Mul(s2, z1z1)
	s2.Mod(s2, curve.P)
	r := new(big.Int).Sub(s2, s1)
	if r.Sign() == -1 {
		r.Add(r, curve.P)
	}
	yEqual := r.Sign() == 0
	if xEqual && yEqual {
		return curve.doubleJacobian(x1, y1, z1)
	}
	r.Lsh(r, 1)
	v := new(big.Int).Mul(u1, i)

	x3.Set(r)
	x3.Mul(x3, x3)
	x3.Sub(x3, j)
	x3.Sub(x3, v)
	x3.Sub(x3, v)
	x3.Mod(x3, curve.P)

	y3.Set(r)
	v.Sub(v, x3)
	y3.Mul(y3, v)
	s1.Mul(s1, j)
	s1.Lsh(s1, 1)
	y3.Sub(y3, s1)
	y3.Mod(y3, curve.P)

	z3.Add(z1, z2)
	z3.Mul(z3, z3)
	z3.Sub(z3, z1z1)
	z3.Sub(z3, z2z2)
	z3.Mul(z3, h)
	z3.Mod(z3, curve.P)

	return x3, y3, z3
}

// Double returns 2*(x1,y1). Part of the elliptic.Curve interface.
func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
	z1 := zForAffine(x1, y1)
	return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
}

// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
// returns its double, also in Jacobian form.
func (curve *KoblitzCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
	// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l

	a := new(big.Int).Mul(x, x) //X1²
	b := new(big.Int).Mul(y, y) //Y1²
	c := new(big.Int).Mul(b, b) //B²

	d := new(big.Int).Add(x, b) //X1+B
	d.Mul(d, d)                 //(X1+B)²
	d.Sub(d, a)                 //(X1+B)²-A
	d.Sub(d, c)                 //(X1+B)²-A-C
	d.Mul(d, big.NewInt(2))     //2*((X1+B)²-A-C)

	e := new(big.Int).Mul(big.NewInt(3), a) //3*A
	f := new(big.Int).Mul(e, e)             //E²

	x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
	x3.Sub(f, x3)                            //F-2*D
	x3.Mod(x3, curve.P)

	y3 := new(big.Int).Sub(d, x3)                  //D-X3
	y3.Mul(e, y3)                                  //E*(D-X3)
	y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
	y3.Mod(y3, curve.P)

	z3 := new(big.Int).Mul(y, z) //Y1*Z1
	z3.Mul(big.NewInt(2), z3)    //3*Y1*Z1
	z3.Mod(z3, curve.P)

	return x3, y3, z3
}

// ScalarMult returns k*(Bx, By) where k is a big endian integer.
// Part of the elliptic.Curve interface.
func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
	Bz := new(big.Int).SetInt64(1)
	x, y, z := new(big.Int), new(big.Int), new(big.Int)

	for _, byte := range k {
		for bitNum := 0; bitNum < 8; bitNum++ {
			x, y, z = curve.doubleJacobian(x, y, z)
			if byte&0x80 == 0x80 {
				x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z)
			}
			byte <<= 1
		}
	}

	return curve.affineFromJacobian(x, y, z)
}

// ScalarBaseMult returns k*G where G is the base point of the group and k is a
// big endian integer.
// Part of the elliptic.Curve interface.
func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
	return curve.ScalarMult(curve.Gx, curve.Gy, k)
}

// QPlus1Div4 returns the Q+1/4 constant for the curve for use in calculating
// square roots via exponention.
func (curve *KoblitzCurve) QPlus1Div4() *big.Int {
	if curve.q == nil {
		curve.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P, big.NewInt(1)), big.NewInt(4))
	}
	return curve.q
}

// Curve parameters taken from: http://www.secg.org/collateral/sec2_final.pdf
var initonce sync.Once
var secp256k1 KoblitzCurve

func initAll() {
	initS256()
}

func initS256() {
	// See SEC 2 section 2.7.1
	secp256k1.CurveParams = new(elliptic.CurveParams)
	secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
	secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
	secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
	secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
	secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
	secp256k1.BitSize = 256
}

// S256 returns a Curve which implements secp256k1.
func S256() *KoblitzCurve {
	initonce.Do(initAll)
	return &secp256k1
}
<commit_msg>remove lazy computation of QPlus1Div4 and do at init time.<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Copyright 2011 ThePiachu. All rights reserved.
// Copyright 2013 Conformal Systems LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package btcec

// This package operates, internally, on Jacobian coordinates. For a given
// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, z1)
// where x = x1/z1² and y = y1/z1³. The greatest speedups come when the whole
// calculation can be performed within the transform (as in ScalarMult and
// ScalarBaseMult). But even for Add and Double, it's faster to apply and
// reverse the transform than to operate in affine coordinates.

import (
	"crypto/elliptic"
	"math/big"
	"sync"
)

//TODO: examine if we need to care about EC optimization as descibed here
//      https://bitcointalk.org/index.php?topic=155054.0;all

// KoblitzCurve supports a koblitz curve implementation that fits the ECC Curve
// interface from crypto/elliptic.
type KoblitzCurve struct {
	*elliptic.CurveParams
	q *big.Int
}

// Params returns the parameters fro the curve.
func (curve *KoblitzCurve) Params() *elliptic.CurveParams {
	return curve.CurveParams
}

// IsOnCurve returns boolean if the point (x,y) is on the curve.
// Part of the elliptic.Curve interface. This function differs from the
// crypto/elliptic algorithm since a = 0 not -3.
func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool {
	// y² = x³ + b
	y2 := new(big.Int).Mul(y, y) //y²
	y2.Mod(y2, curve.P)          //y²%P

	x3 := new(big.Int).Mul(x, x) //x²
	x3.Mul(x3, x)                //x³

	x3.Add(x3, curve.B) //x³+B
	x3.Mod(x3, curve.P) //(x³+B)%P

	return x3.Cmp(y2) == 0
}

// zForAffine returns a Jacobian Z value for the affine point (x, y). If x and
// y are zero, it assumes that they represent the point at infinity because (0,
// 0) is not on the any of the curves handled here.
func zForAffine(x, y *big.Int) *big.Int {
	z := new(big.Int)
	if x.Sign() != 0 || y.Sign() != 0 {
		z.SetInt64(1)
	}
	return z
}

// affineFromJacobian reverses the Jacobian transform. See the comment at the
// top of the file. If the point is ∞ it returns 0, 0.
func (curve *KoblitzCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) {
	if z.Sign() == 0 {
		return new(big.Int), new(big.Int)
	}

	zinv := new(big.Int).ModInverse(z, curve.P)
	zinvsq := new(big.Int).Mul(zinv, zinv)

	xOut = new(big.Int).Mul(x, zinvsq)
	xOut.Mod(xOut, curve.P)
	zinvsq.Mul(zinvsq, zinv)
	yOut = new(big.Int).Mul(y, zinvsq)
	yOut.Mod(yOut, curve.P)
	return
}

// Add returns the sum of (x1,y1 and (x2,y2). Part of the elliptic.Curve
// interface.
func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) {
	z1 := zForAffine(x1, y1)
	z2 := zForAffine(x2, y2)
	return curve.affineFromJacobian(curve.addJacobian(x1, y1, z1, x2, y2, z2))
}

// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and
// (x2, y2, z2) and returns their sum, also in Jacobian form.
func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) {
	// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl
	x3, y3, z3 := new(big.Int), new(big.Int), new(big.Int)
	if z1.Sign() == 0 {
		x3.Set(x2)
		y3.Set(y2)
		z3.Set(z2)
		return x3, y3, z3
	}
	if z2.Sign() == 0 {
		x3.Set(x1)
		y3.Set(y1)
		z3.Set(z1)
		return x3, y3, z3
	}

	z1z1 := new(big.Int).Mul(z1, z1)
	z1z1.Mod(z1z1, curve.P)
	z2z2 := new(big.Int).Mul(z2, z2)
	z2z2.Mod(z2z2, curve.P)

	u1 := new(big.Int).Mul(x1, z2z2)
	u1.Mod(u1, curve.P)
	u2 := new(big.Int).Mul(x2, z1z1)
	u2.Mod(u2, curve.P)
	h := new(big.Int).Sub(u2, u1)
	xEqual := h.Sign() == 0
	if h.Sign() == -1 {
		h.Add(h, curve.P)
	}
	i := new(big.Int).Lsh(h, 1)
	i.Mul(i, i)
	j := new(big.Int).Mul(h, i)

	s1 := new(big.Int).Mul(y1, z2)
	s1.Mul(s1, z2z2)
	s1.Mod(s1, curve.P)
	s2 := new(big.Int).Mul(y2, z1)
	s2.Mul(s2, z1z1)
	s2.Mod(s2, curve.P)
	r := new(big.Int).Sub(s2, s1)
	if r.Sign() == -1 {
		r.Add(r, curve.P)
	}
	yEqual := r.Sign() == 0
	if xEqual && yEqual {
		return curve.doubleJacobian(x1, y1, z1)
	}
	r.Lsh(r, 1)
	v := new(big.Int).Mul(u1, i)

	x3.Set(r)
	x3.Mul(x3, x3)
	x3.Sub(x3, j)
	x3.Sub(x3, v)
	x3.Sub(x3, v)
	x3.Mod(x3, curve.P)

	y3.Set(r)
	v.Sub(v, x3)
	y3.Mul(y3, v)
	s1.Mul(s1, j)
	s1.Lsh(s1, 1)
	y3.Sub(y3, s1)
	y3.Mod(y3, curve.P)

	z3.Add(z1, z2)
	z3.Mul(z3, z3)
	z3.Sub(z3, z1z1)
	z3.Sub(z3, z2z2)
	z3.Mul(z3, h)
	z3.Mod(z3, curve.P)

	return x3, y3, z3
}

// Double returns 2*(x1,y1). Part of the elliptic.Curve interface.
func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) {
	z1 := zForAffine(x1, y1)
	return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1))
}

// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and
// returns its double, also in Jacobian form.
func (curve *KoblitzCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) {
	// See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l

	a := new(big.Int).Mul(x, x) //X1²
	b := new(big.Int).Mul(y, y) //Y1²
	c := new(big.Int).Mul(b, b) //B²

	d := new(big.Int).Add(x, b) //X1+B
	d.Mul(d, d)                 //(X1+B)²
	d.Sub(d, a)                 //(X1+B)²-A
	d.Sub(d, c)                 //(X1+B)²-A-C
	d.Mul(d, big.NewInt(2))     //2*((X1+B)²-A-C)

	e := new(big.Int).Mul(big.NewInt(3), a) //3*A
	f := new(big.Int).Mul(e, e)             //E²

	x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D
	x3.Sub(f, x3)                            //F-2*D
	x3.Mod(x3, curve.P)

	y3 := new(big.Int).Sub(d, x3)                  //D-X3
	y3.Mul(e, y3)                                  //E*(D-X3)
	y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C
	y3.Mod(y3, curve.P)

	z3 := new(big.Int).Mul(y, z) //Y1*Z1
	z3.Mul(big.NewInt(2), z3)    //3*Y1*Z1
	z3.Mod(z3, curve.P)

	return x3, y3, z3
}

// ScalarMult returns k*(Bx, By) where k is a big endian integer.
// Part of the elliptic.Curve interface.
func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) {
	Bz := new(big.Int).SetInt64(1)
	x, y, z := new(big.Int), new(big.Int), new(big.Int)

	for _, byte := range k {
		for bitNum := 0; bitNum < 8; bitNum++ {
			x, y, z = curve.doubleJacobian(x, y, z)
			if byte&0x80 == 0x80 {
				x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z)
			}
			byte <<= 1
		}
	}

	return curve.affineFromJacobian(x, y, z)
}

// ScalarBaseMult returns k*G where G is the base point of the group and k is a
// big endian integer.
// Part of the elliptic.Curve interface.
func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) {
	return curve.ScalarMult(curve.Gx, curve.Gy, k)
}

// QPlus1Div4 returns the Q+1/4 constant for the curve for use in calculating
// square roots via exponention.
func (curve *KoblitzCurve) QPlus1Div4() *big.Int {
	return curve.q
}

// Curve parameters taken from: http://www.secg.org/collateral/sec2_final.pdf
var initonce sync.Once
var secp256k1 KoblitzCurve

func initAll() {
	initS256()
}

func initS256() {
	// See SEC 2 section 2.7.1
	secp256k1.CurveParams = new(elliptic.CurveParams)
	secp256k1.P, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16)
	secp256k1.N, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16)
	secp256k1.B, _ = new(big.Int).SetString("0000000000000000000000000000000000000000000000000000000000000007", 16)
	secp256k1.Gx, _ = new(big.Int).SetString("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16)
	secp256k1.Gy, _ = new(big.Int).SetString("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)
	secp256k1.BitSize = 256
	secp256k1.q = new(big.Int).Div(new(big.Int).Add(secp256k1.P,
		big.NewInt(1)), big.NewInt(4))
}

// S256 returns a Curve which implements secp256k1.
func S256() *KoblitzCurve {
	initonce.Do(initAll)
	return &secp256k1
}
<|endoftext|>


<commit_before>package rpc

import (
	"fmt"
	"net"
	"testing"

	"github.com/hashicorp/yamux"
)

func TestMuxBroker(t *testing.T) {
	c, s := testYamux(t)
	defer c.Close()
	defer s.Close()

	bc := newMuxBroker(c)
	bs := newMuxBroker(s)
	go bc.Run()
	go bs.Run()

	errChan := make(chan error, 2)
	go func() {
		defer close(errChan)
		c, err := bc.Dial(5)
		if err != nil {
			errChan <- fmt.Errorf("err dialing: %s", err.Error())
			return
		}

		if _, err := c.Write([]byte{42}); err != nil {
			errChan <- fmt.Errorf("err writing: %s", err.Error())
		}
	}()

	client, err := bs.Accept(5)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	var data [1]byte
	if _, err := client.Read(data[:]); err != nil {
		t.Fatalf("err: %s", err)
	}

	if data[0] != 42 {
		t.Fatalf("bad: %d", data[0])
	}

	for err := range errChan {
		if err != nil {
			t.Fatalf(err.Error())
		}
	}
}

func testYamux(t *testing.T) (client *yamux.Session, server *yamux.Session) {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Server side
	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)
		conn, err := l.Accept()
		l.Close()
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		server, err = yamux.Server(conn, nil)
		if err != nil {
			t.Fatalf("err: %s", err)
		}
	}()

	// Client side
	conn, err := net.Dial("tcp", l.Addr().String())
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	client, err = yamux.Client(conn, nil)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Wait for the server
	<-doneCh

	return
}
<commit_msg>Revert "mux_broker_test.go: make non blocking errChan"<commit_after>package rpc

import (
	"fmt"
	"net"
	"testing"

	"github.com/hashicorp/yamux"
)

func TestMuxBroker(t *testing.T) {
	c, s := testYamux(t)
	defer c.Close()
	defer s.Close()

	bc := newMuxBroker(c)
	bs := newMuxBroker(s)
	go bc.Run()
	go bs.Run()

	errChan := make(chan error, 1)
	go func() {
		defer close(errChan)
		c, err := bc.Dial(5)
		if err != nil {
			errChan <- fmt.Errorf("err dialing: %s", err.Error())
			return
		}

		if _, err := c.Write([]byte{42}); err != nil {
			errChan <- fmt.Errorf("err writing: %s", err.Error())
		}
	}()

	client, err := bs.Accept(5)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	var data [1]byte
	if _, err := client.Read(data[:]); err != nil {
		t.Fatalf("err: %s", err)
	}

	if data[0] != 42 {
		t.Fatalf("bad: %d", data[0])
	}

	for err := range errChan {
		if err != nil {
			t.Fatalf(err.Error())
		}
	}
}

func testYamux(t *testing.T) (client *yamux.Session, server *yamux.Session) {
	l, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Server side
	doneCh := make(chan struct{})
	go func() {
		defer close(doneCh)
		conn, err := l.Accept()
		l.Close()
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		server, err = yamux.Server(conn, nil)
		if err != nil {
			t.Fatalf("err: %s", err)
		}
	}()

	// Client side
	conn, err := net.Dial("tcp", l.Addr().String())
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	client, err = yamux.Client(conn, nil)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	// Wait for the server
	<-doneCh

	return
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"flag"
	"fmt"
	. "github.com/timeredbull/tsuru/api/service"
	"github.com/timeredbull/tsuru/db"
	"log"
	"os"
	"runtime/pprof"
)

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var memprofile = flag.String("memprofile", "", "write memory profile to file")

func main() {
	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	var err error
	db.Session, err = db.Open("127.0.0.1:27017", "tsuru")
	if err != nil {
		panic(err)
	}
	defer db.Session.Close()

	sType := &ServiceType{Name: "Mysql", Charm: "mysql"}
	sType.Create()
	var s Service
	var name string
	for i := 0; i < 1000; i++ {
		name = fmt.Sprintf("myService%d", i)
		s = Service{ServiceTypeId: sType.Id, Name: name}
		s.Create()
	}
	s = Service{}
	s.All()

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			log.Fatal(err)
		}
		pprof.WriteHeapProfile(f)
		f.Close()
	}
}
<commit_msg>api/service: remove dead code<commit_after><|endoftext|>


<commit_before>// Copyright The OpenTelemetry 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 testutil

import (
	"net"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"testing"

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

type portpair struct {
	first string
	last  string
}

// GetAvailableLocalAddress finds an available local port and returns an endpoint
// describing it. The port is available for opening when this function returns
// provided that there is no race by some other code to grab the same port
// immediately.
func GetAvailableLocalAddress(t *testing.T) string {
	ln, err := net.Listen("tcp", "localhost:0")
	require.NoError(t, err, "Failed to get a free local port")
	// There is a possible race if something else takes this same port before
	// the test uses it, however, that is unlikely in practice.
	defer ln.Close()
	return ln.Addr().String()
}

// GetAvailablePort finds an available local port and returns it. The port is
// available for opening when this function returns provided that there is no
// race by some other code to grab the same port immediately.
func GetAvailablePort(t *testing.T) uint16 {
	// Retry has been added for windows as net.Listen can return a port that is not actually available. Details can be
	// found in https://github.com/docker/for-win/issues/3171 but to summarize Hyper-V will reserve ranges of ports
	// which do not show up under the "netstat -ano" but can only be found by
	// "netsh interface ipv4 show excludedportrange protocol=tcp".  We'll use []exclusions to hold those ranges and
	// retry if the port returned by GetAvailableLocalAddress falls in one of those them.
	var exclusions []portpair
	portFound := false
	var port string
	var err error
	if runtime.GOOS == "windows" {
		exclusions = getExclusionsList(t)
	}

	for !portFound {
		endpoint := GetAvailableLocalAddress(t)
		_, port, err = net.SplitHostPort(endpoint)
		require.NoError(t, err)
		portFound = true
		if runtime.GOOS == "windows" {
			for _, pair := range exclusions {
				if port >= pair.first && port <= pair.last {
					portFound = false
					break
				}
			}
		}
	}

	portInt, err := strconv.Atoi(port)
	require.NoError(t, err)

	return uint16(portInt)
}

// Get excluded ports on Windows from the command: netsh interface ipv4 show excludedportrange protocol=tcp
func getExclusionsList(t *testing.T) []portpair {
	cmd := exec.Command("netsh", "interface", "ipv4", "show", "excludedportrange", "protocol=tcp")
	output, err := cmd.CombinedOutput()
	require.NoError(t, err)

	exclusions := createExclusionsList(string(output), t)
	return exclusions
}

func createExclusionsList(exclusionsText string, t *testing.T) []portpair {
	exclusions := []portpair{}

	parts := strings.Split(exclusionsText, "--------")
	require.Equal(t, len(parts), 3)
	portsText := strings.Split(parts[2], "*")
	require.Greater(t, len(portsText), 1) // original text may have a suffix like " - Administered port exclusions."
	lines := strings.Split(portsText[0], "\n")
	for _, line := range lines {
		if strings.TrimSpace(line) != "" {
			entries := strings.Fields(strings.TrimSpace(line))
			require.Equal(t, len(entries), 2)
			pair := portpair{entries[0], entries[1]}
			exclusions = append(exclusions, pair)
		}
	}
	return exclusions
}
<commit_msg>Avoid potential integer overflow (#5948)<commit_after>// Copyright The OpenTelemetry 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 testutil

import (
	"net"
	"os/exec"
	"runtime"
	"strconv"
	"strings"
	"testing"

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

type portpair struct {
	first string
	last  string
}

// GetAvailableLocalAddress finds an available local port and returns an endpoint
// describing it. The port is available for opening when this function returns
// provided that there is no race by some other code to grab the same port
// immediately.
func GetAvailableLocalAddress(t *testing.T) string {
	ln, err := net.Listen("tcp", "localhost:0")
	require.NoError(t, err, "Failed to get a free local port")
	// There is a possible race if something else takes this same port before
	// the test uses it, however, that is unlikely in practice.
	defer ln.Close()
	return ln.Addr().String()
}

// GetAvailablePort finds an available local port and returns it. The port is
// available for opening when this function returns provided that there is no
// race by some other code to grab the same port immediately.
func GetAvailablePort(t *testing.T) uint16 {
	// Retry has been added for windows as net.Listen can return a port that is not actually available. Details can be
	// found in https://github.com/docker/for-win/issues/3171 but to summarize Hyper-V will reserve ranges of ports
	// which do not show up under the "netstat -ano" but can only be found by
	// "netsh interface ipv4 show excludedportrange protocol=tcp".  We'll use []exclusions to hold those ranges and
	// retry if the port returned by GetAvailableLocalAddress falls in one of those them.
	var exclusions []portpair
	portFound := false
	var port string
	var err error
	if runtime.GOOS == "windows" {
		exclusions = getExclusionsList(t)
	}

	for !portFound {
		endpoint := GetAvailableLocalAddress(t)
		_, port, err = net.SplitHostPort(endpoint)
		require.NoError(t, err)
		portFound = true
		if runtime.GOOS == "windows" {
			for _, pair := range exclusions {
				if port >= pair.first && port <= pair.last {
					portFound = false
					break
				}
			}
		}
	}

	portInt, err := strconv.ParseUint(port, 10, 16)
	require.NoError(t, err)

	return uint16(portInt)
}

// Get excluded ports on Windows from the command: netsh interface ipv4 show excludedportrange protocol=tcp
func getExclusionsList(t *testing.T) []portpair {
	cmd := exec.Command("netsh", "interface", "ipv4", "show", "excludedportrange", "protocol=tcp")
	output, err := cmd.CombinedOutput()
	require.NoError(t, err)

	exclusions := createExclusionsList(string(output), t)
	return exclusions
}

func createExclusionsList(exclusionsText string, t *testing.T) []portpair {
	exclusions := []portpair{}

	parts := strings.Split(exclusionsText, "--------")
	require.Equal(t, len(parts), 3)
	portsText := strings.Split(parts[2], "*")
	require.Greater(t, len(portsText), 1) // original text may have a suffix like " - Administered port exclusions."
	lines := strings.Split(portsText[0], "\n")
	for _, line := range lines {
		if strings.TrimSpace(line) != "" {
			entries := strings.Fields(strings.TrimSpace(line))
			require.Equal(t, len(entries), 2)
			pair := portpair{entries[0], entries[1]}
			exclusions = append(exclusions, pair)
		}
	}
	return exclusions
}
<|endoftext|>


<commit_before>// +build libpfm,cgo

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

// Collector of perf events for a container.
package perf

import (
	"bytes"
	"encoding/binary"
	"testing"

	"github.com/stretchr/testify/assert"

	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/stats"
)

type buffer struct {
	*bytes.Buffer
}

func (b buffer) Close() error {
	return nil
}

func TestCollector_UpdateStats(t *testing.T) {
	collector := collector{uncore: &stats.NoopCollector{}}
	notScaledBuffer := buffer{bytes.NewBuffer([]byte{})}
	scaledBuffer := buffer{bytes.NewBuffer([]byte{})}
	err := binary.Write(notScaledBuffer, binary.LittleEndian, ReadFormat{
		Value:       123456789,
		TimeEnabled: 100,
		TimeRunning: 100,
		ID:          1,
	})
	assert.NoError(t, err)
	err = binary.Write(scaledBuffer, binary.LittleEndian, ReadFormat{
		Value:       333333333,
		TimeEnabled: 3,
		TimeRunning: 1,
		ID:          2,
	})
	assert.NoError(t, err)
	collector.cpuFiles = map[string]map[int]readerCloser{
		"instructions": {0: notScaledBuffer},
		"cycles":       {11: scaledBuffer},
	}

	stats := &info.ContainerStats{}
	err = collector.UpdateStats(stats)

	assert.NoError(t, err)
	assert.Len(t, stats.PerfStats, 2)

	assert.Contains(t, stats.PerfStats, info.PerfStat{
		PerfValue: info.PerfValue{
			ScalingRatio: 0.3333333333333333,
			Value:        999999999,
			Name:         "cycles",
		},
		Cpu: 11,
	})
	assert.Contains(t, stats.PerfStats, info.PerfStat{
		PerfValue: info.PerfValue{
			ScalingRatio: 1,
			Value:        123456789,
			Name:         "instructions",
		},
		Cpu: 0,
	})
}

func TestCreatePerfEventAttr(t *testing.T) {
	event := CustomEvent{
		Type:   0x1,
		Config: Config{uint64(0x2), uint64(0x3), uint64(0x4)},
		Name:   "fake_event",
	}

	attributes := createPerfEventAttr(event)

	assert.Equal(t, uint32(1), attributes.Type)
	assert.Equal(t, uint64(2), attributes.Config)
	assert.Equal(t, uint64(3), attributes.Ext1)
	assert.Equal(t, uint64(4), attributes.Ext2)
	assert.Equal(t, uint64(65536), attributes.Sample_type)
	assert.Equal(t, uint64(7), attributes.Read_format)
	assert.Equal(t, uint64(1048578), attributes.Bits)
}

func TestNewCollector(t *testing.T) {
	perfCollector := newCollector("cgroup", PerfEvents{
		Core: Events{
			Events: [][]Event{{"event_1"}, {"event_2"}},
			CustomEvents: []CustomEvent{{
				Type:   0,
				Config: []uint64{1, 2, 3},
				Name:   "event_2",
			}},
		},
	}, 1, []info.Node{})
	assert.Len(t, perfCollector.eventToCustomEvent, 1)
	assert.Nil(t, perfCollector.eventToCustomEvent[Event("event_1")])
	assert.Same(t, &perfCollector.events.Core.CustomEvents[0], perfCollector.eventToCustomEvent[Event("event_2")])
}

var readPerfStatCases = []struct {
	test     string
	file     ReadFormat
	name     string
	cpu      int
	perfStat info.PerfStat
	err      error
}{
	{
		test: "no scaling",
		file: ReadFormat{
			Value:       5,
			TimeEnabled: 0,
			TimeRunning: 0,
			ID:          0,
		},
		name: "some metric",
		cpu:  1,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1,
				Value:        5,
				Name:         "some metric",
			},
			Cpu: 1,
		},
		err: nil,
	},
	{
		test: "no scaling - TimeEnabled = 0",
		file: ReadFormat{
			Value:       5,
			TimeEnabled: 0,
			TimeRunning: 1,
			ID:          0,
		},
		name: "some metric",
		cpu:  1,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1,
				Value:        5,
				Name:         "some metric",
			},
			Cpu: 1,
		},
		err: nil,
	},
	{
		test: "scaling - 0.5",
		file: ReadFormat{
			Value:       4,
			TimeEnabled: 4,
			TimeRunning: 2,
			ID:          0,
		},
		name: "some metric",
		cpu:  2,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 0.5,
				Value:        8,
				Name:         "some metric",
			},
			Cpu: 2,
		},
		err: nil,
	},
	{
		test: "scaling - 0 (TimeEnabled = 1, TimeRunning = 0)",
		file: ReadFormat{
			Value:       4,
			TimeEnabled: 1,
			TimeRunning: 0,
			ID:          0,
		},
		name: "some metric",
		cpu:  3,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        4,
				Name:         "some metric",
			},
			Cpu: 3,
		},
		err: nil,
	},
	{
		test: "scaling - 0 (TimeEnabled = 0, TimeRunning = 1)",
		file: ReadFormat{
			Value:       4,
			TimeEnabled: 0,
			TimeRunning: 1,
			ID:          0,
		},
		name: "some metric",
		cpu:  3,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        4,
				Name:         "some metric",
			},
			Cpu: 3,
		},
		err: nil,
	},
	{
		test: "zeros, zeros everywhere",
		file: ReadFormat{
			Value:       0,
			TimeEnabled: 0,
			TimeRunning: 0,
			ID:          0,
		},
		name: "some metric",
		cpu:  4,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        0,
				Name:         "some metric",
			},
			Cpu: 4,
		},
		err: nil,
	},
}

func TestReadPerfStat(t *testing.T) {
	for _, test := range readPerfStatCases {
		t.Run(test.test, func(tt *testing.T) {
			buf := &buffer{bytes.NewBuffer([]byte{})}
			err := binary.Write(buf, binary.LittleEndian, test.file)
			assert.NoError(tt, err)
			stat, err := readPerfStat(buf, test.name, test.cpu)
			assert.Equal(tt, test.perfStat, *stat)
			assert.Equal(tt, test.err, err)
		})
	}
}
<commit_msg>Adding extra test case<commit_after>// +build libpfm,cgo

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

// Collector of perf events for a container.
package perf

import (
	"bytes"
	"encoding/binary"
	"testing"

	"github.com/stretchr/testify/assert"

	info "github.com/google/cadvisor/info/v1"
	"github.com/google/cadvisor/stats"
)

type buffer struct {
	*bytes.Buffer
}

func (b buffer) Close() error {
	return nil
}

func TestCollector_UpdateStats(t *testing.T) {
	collector := collector{uncore: &stats.NoopCollector{}}
	notScaledBuffer := buffer{bytes.NewBuffer([]byte{})}
	scaledBuffer := buffer{bytes.NewBuffer([]byte{})}
	err := binary.Write(notScaledBuffer, binary.LittleEndian, ReadFormat{
		Value:       123456789,
		TimeEnabled: 100,
		TimeRunning: 100,
		ID:          1,
	})
	assert.NoError(t, err)
	err = binary.Write(scaledBuffer, binary.LittleEndian, ReadFormat{
		Value:       333333333,
		TimeEnabled: 3,
		TimeRunning: 1,
		ID:          2,
	})
	assert.NoError(t, err)
	collector.cpuFiles = map[string]map[int]readerCloser{
		"instructions": {0: notScaledBuffer},
		"cycles":       {11: scaledBuffer},
	}

	stats := &info.ContainerStats{}
	err = collector.UpdateStats(stats)

	assert.NoError(t, err)
	assert.Len(t, stats.PerfStats, 2)

	assert.Contains(t, stats.PerfStats, info.PerfStat{
		PerfValue: info.PerfValue{
			ScalingRatio: 0.3333333333333333,
			Value:        999999999,
			Name:         "cycles",
		},
		Cpu: 11,
	})
	assert.Contains(t, stats.PerfStats, info.PerfStat{
		PerfValue: info.PerfValue{
			ScalingRatio: 1,
			Value:        123456789,
			Name:         "instructions",
		},
		Cpu: 0,
	})
}

func TestCreatePerfEventAttr(t *testing.T) {
	event := CustomEvent{
		Type:   0x1,
		Config: Config{uint64(0x2), uint64(0x3), uint64(0x4)},
		Name:   "fake_event",
	}

	attributes := createPerfEventAttr(event)

	assert.Equal(t, uint32(1), attributes.Type)
	assert.Equal(t, uint64(2), attributes.Config)
	assert.Equal(t, uint64(3), attributes.Ext1)
	assert.Equal(t, uint64(4), attributes.Ext2)
	assert.Equal(t, uint64(65536), attributes.Sample_type)
	assert.Equal(t, uint64(7), attributes.Read_format)
	assert.Equal(t, uint64(1048578), attributes.Bits)
}

func TestNewCollector(t *testing.T) {
	perfCollector := newCollector("cgroup", PerfEvents{
		Core: Events{
			Events: [][]Event{{"event_1"}, {"event_2"}},
			CustomEvents: []CustomEvent{{
				Type:   0,
				Config: []uint64{1, 2, 3},
				Name:   "event_2",
			}},
		},
	}, 1, []info.Node{})
	assert.Len(t, perfCollector.eventToCustomEvent, 1)
	assert.Nil(t, perfCollector.eventToCustomEvent[Event("event_1")])
	assert.Same(t, &perfCollector.events.Core.CustomEvents[0], perfCollector.eventToCustomEvent[Event("event_2")])
}

var readPerfStatCases = []struct {
	test     string
	file     ReadFormat
	name     string
	cpu      int
	perfStat info.PerfStat
	err      error
}{
	{
		test: "no scaling",
		file: ReadFormat{
			Value:       5,
			TimeEnabled: 0,
			TimeRunning: 0,
			ID:          0,
		},
		name: "some metric",
		cpu:  1,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1,
				Value:        5,
				Name:         "some metric",
			},
			Cpu: 1,
		},
		err: nil,
	},
	{
		test: "no scaling - TimeEnabled = 0",
		file: ReadFormat{
			Value:       5,
			TimeEnabled: 0,
			TimeRunning: 1,
			ID:          0,
		},
		name: "some metric",
		cpu:  1,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1,
				Value:        5,
				Name:         "some metric",
			},
			Cpu: 1,
		},
		err: nil,
	},
	{
		test: "scaling - 0.5",
		file: ReadFormat{
			Value:       4,
			TimeEnabled: 4,
			TimeRunning: 2,
			ID:          0,
		},
		name: "some metric",
		cpu:  2,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 0.5,
				Value:        8,
				Name:         "some metric",
			},
			Cpu: 2,
		},
		err: nil,
	},
	{
		test: "scaling - 0 (TimeEnabled = 1, TimeRunning = 0)",
		file: ReadFormat{
			Value:       4,
			TimeEnabled: 1,
			TimeRunning: 0,
			ID:          0,
		},
		name: "some metric",
		cpu:  3,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        4,
				Name:         "some metric",
			},
			Cpu: 3,
		},
		err: nil,
	},
	{
		test: "scaling - 0 (TimeEnabled = 0, TimeRunning = 1)",
		file: ReadFormat{
			Value:       4,
			TimeEnabled: 0,
			TimeRunning: 1,
			ID:          0,
		},
		name: "some metric",
		cpu:  3,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        4,
				Name:         "some metric",
			},
			Cpu: 3,
		},
		err: nil,
	},
	{
		test: "zeros, zeros everywhere",
		file: ReadFormat{
			Value:       0,
			TimeEnabled: 0,
			TimeRunning: 0,
			ID:          0,
		},
		name: "some metric",
		cpu:  4,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        0,
				Name:         "some metric",
			},
			Cpu: 4,
		},
		err: nil,
	},
	{
		test: "non-zero TimeRunning",
		file: ReadFormat{
			Value:       0,
			TimeEnabled: 0,
			TimeRunning: 3,
			ID:          0,
		},
		name: "some metric",
		cpu:  4,
		perfStat: info.PerfStat{
			PerfValue: info.PerfValue{
				ScalingRatio: 1.0,
				Value:        0,
				Name:         "some metric",
			},
			Cpu: 4,
		},
		err: nil,
	},
}

func TestReadPerfStat(t *testing.T) {
	for _, test := range readPerfStatCases {
		t.Run(test.test, func(tt *testing.T) {
			buf := &buffer{bytes.NewBuffer([]byte{})}
			err := binary.Write(buf, binary.LittleEndian, test.file)
			assert.NoError(tt, err)
			stat, err := readPerfStat(buf, test.name, test.cpu)
			assert.Equal(tt, test.perfStat, *stat)
			assert.Equal(tt, test.err, err)
		})
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package logger

import (
	"log"
	"reflect"
	"testing"
	"time"
)

func TestBridgeLogPgk(t *testing.T) {
	defer reset()

	tags := Tags{"TestSetLogOutput", "log"}
	ew := eventWriter{}
	Start(&ew)
	BridgeLogPgk(tags)

	t1 := time.Now()

	defer func() {
		if recv := recover(); recv == nil {
			t.Fatalf("Expected an panic to occur, but it didn't")
		}

		if err := Close(); err != nil {
			t.Fatal("Unexpected error calling close: ", err.Error())
		}

		expected := []Event{
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Log message"},
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Log formatted message"},
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Log message newline"},
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Panic message"},
		}

		if len(ew.events) != len(expected) {
			t.Fatalf("Expected to have %d events, but got %d",
				len(expected), len(ew.events))
		}

		const margin = 100 * time.Millisecond
		for i, event := range ew.events {
			expectedEvent := expected[i]

			// Can't mock time in the log package, so we'll make sure it falls within
			// the margin.
			if event.Timestamp.Sub(expectedEvent.Timestamp) > margin {
				t.Errorf("Expected event #%d to be %v, but got %v", i, expectedEvent, event)
				continue
			}
			event.Timestamp = expectedEvent.Timestamp

			if expected, got := expectedEvent, event; !reflect.DeepEqual(expected, got) {
				t.Errorf("Expected event #%d to be %v, but got %v", i, expected, got)
			}
		}
	}()

	log.Print("Log message")
	log.Printf("Log %s message", "formatted")
	log.Println("Log message newline")
	log.Panic("Panic message")
}

func TestLogToEventError(t *testing.T) {
	defer reset()

	tags := Tags{"TestLogToEventError"}
	w := logToEvent{tags, time.Now().Location()}

	t1 := now()
	line1 := "otherPrefix:2015/11/18 22:07:20.284275\n"
	w.Write([]byte(line1))
	line2 := logPrefix + "\n"
	w.Write([]byte(line2))
	line3 := logPrefix + "2015/18/11 22:07:20.284275\n"
	w.Write([]byte(line3)) // yyyy/dd/mm
	close(eventChannel)

	expected := []Event{
		{Type: ErrorEvent, Timestamp: t1, Tags: tags, Message: ErrLogFormat.Error(), Data: line1},
		{Type: ErrorEvent, Timestamp: t1, Tags: tags, Message: ErrLogFormat.Error(), Data: line2},
		{Type: ErrorEvent, Timestamp: t1, Tags: tags, Message: "parsing time \"2015/18/11 22:07:20.284275\": month out of range", Data: line3},
	}

	const margin = 100 * time.Millisecond
	i := 0
	for event := range eventChannel {
		expectedEvent := expected[i]
		i++

		// Can't mock time in the log package, so we have a truncate it.
		if !event.Timestamp.Truncate(margin).Equal(expectedEvent.Timestamp.Truncate(margin)) {
			t.Errorf("Expected event #%d to be %v, but got %v", i, expectedEvent, event)
			continue
		}
		event.Timestamp = expectedEvent.Timestamp

		if expected, got := expectedEvent, event; !reflect.DeepEqual(expected, got) {
			t.Errorf("Expected event #%d to be %v, but got %v", i, expected, got)
		}
	}
}
<commit_msg>Reduce TestBridgeLogPgk margin to 1 millisecond<commit_after>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

package logger

import (
	"log"
	"reflect"
	"testing"
	"time"
)

func TestBridgeLogPgk(t *testing.T) {
	defer reset()

	tags := Tags{"TestSetLogOutput", "log"}
	ew := eventWriter{}
	Start(&ew)
	BridgeLogPgk(tags)

	t1 := time.Now()

	defer func() {
		if recv := recover(); recv == nil {
			t.Fatalf("Expected an panic to occur, but it didn't")
		}

		if err := Close(); err != nil {
			t.Fatal("Unexpected error calling close: ", err.Error())
		}

		expected := []Event{
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Log message"},
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Log formatted message"},
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Log message newline"},
			{Type: LogEvent, Timestamp: t1, Tags: tags, Message: "Panic message"},
		}

		if len(ew.events) != len(expected) {
			t.Fatalf("Expected to have %d events, but got %d",
				len(expected), len(ew.events))
		}

		const margin = time.Millisecond
		for i, event := range ew.events {
			expectedEvent := expected[i]

			// Can't mock time in the log package, so we'll make sure it falls within
			// the margin.
			if event.Timestamp.Sub(expectedEvent.Timestamp) > margin {
				t.Errorf("Expected event #%d to be %v, but got %v", i, expectedEvent, event)
				continue
			}
			event.Timestamp = expectedEvent.Timestamp

			if expected, got := expectedEvent, event; !reflect.DeepEqual(expected, got) {
				t.Errorf("Expected event #%d to be %v, but got %v", i, expected, got)
			}
		}
	}()

	log.Print("Log message")
	log.Printf("Log %s message", "formatted")
	log.Println("Log message newline")
	log.Panic("Panic message")
}

func TestLogToEventError(t *testing.T) {
	defer reset()

	tags := Tags{"TestLogToEventError"}
	w := logToEvent{tags, time.Now().Location()}

	t1 := now()
	line1 := "otherPrefix:2015/11/18 22:07:20.284275\n"
	w.Write([]byte(line1))
	line2 := logPrefix + "\n"
	w.Write([]byte(line2))
	line3 := logPrefix + "2015/18/11 22:07:20.284275\n"
	w.Write([]byte(line3)) // yyyy/dd/mm
	close(eventChannel)

	expected := []Event{
		{Type: ErrorEvent, Timestamp: t1, Tags: tags, Message: ErrLogFormat.Error(), Data: line1},
		{Type: ErrorEvent, Timestamp: t1, Tags: tags, Message: ErrLogFormat.Error(), Data: line2},
		{Type: ErrorEvent, Timestamp: t1, Tags: tags, Message: "parsing time \"2015/18/11 22:07:20.284275\": month out of range", Data: line3},
	}

	const margin = 100 * time.Millisecond
	i := 0
	for event := range eventChannel {
		expectedEvent := expected[i]
		i++

		// Can't mock time in the log package, so we have a truncate it.
		if !event.Timestamp.Truncate(margin).Equal(expectedEvent.Timestamp.Truncate(margin)) {
			t.Errorf("Expected event #%d to be %v, but got %v", i, expectedEvent, event)
			continue
		}
		event.Timestamp = expectedEvent.Timestamp

		if expected, got := expectedEvent, event; !reflect.DeepEqual(expected, got) {
			t.Errorf("Expected event #%d to be %v, but got %v", i, expected, got)
		}
	}
}
<|endoftext|>


<commit_before>package expr

import (
	"fmt"
	"math"
	"strings"

	"github.com/grafana/metrictank/batch"
	"github.com/grafana/metrictank/schema"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/consolidation"
	"github.com/raintank/dur"
)

type FuncMovingWindow struct {
	in           GraphiteFunc
	windowSize   string
	fn           string
	xFilesFactor float64

	shiftOffset uint32
}

// NewMovingWindowConstructor takes an agg string and returns a constructor function
func NewMovingWindowConstructor(name string) func() GraphiteFunc {
	return func() GraphiteFunc {
		return &FuncMovingWindow{fn: name}
	}
}

func NewMovingWindow() GraphiteFunc {
	return &FuncMovingWindow{fn: "average", xFilesFactor: 0}
}

func (s *FuncMovingWindow) Signature() ([]Arg, []Arg) {
	return []Arg{
			ArgSeriesList{val: &s.in},
			ArgString{key: "windowSize", val: &s.windowSize, validator: []Validator{IsSignedIntervalString}},
			ArgString{key: "func", opt: true, val: &s.fn, validator: []Validator{IsConsolFunc}},
			ArgFloat{key: "xFilesFactor", opt: true, val: &s.xFilesFactor, validator: []Validator{WithinZeroOneInclusiveInterval}},
		}, []Arg{
			ArgSeriesList{},
		}
}

func (s *FuncMovingWindow) Context(context Context) Context {
	var err error
	s.shiftOffset, err = s.getWindowSeconds()
	if err != nil {
		// shouldn't happen, validated above
		return context
	}

	// Adjust to fetch from the shifted context.from with
	// context.to unchanged
	context.from -= s.shiftOffset
	return context
}

func (s *FuncMovingWindow) Exec(dataMap DataMap) ([]models.Series, error) {
	series, err := s.in.Exec(dataMap)
	if err != nil {
		return nil, err
	}

	aggFunc := consolidation.GetAggFunc(consolidation.FromConsolidateBy(s.fn))
	aggFuncName := fmt.Sprintf("moving%s", strings.Title(s.fn))
	formatStr := fmt.Sprintf("%s(%%s,\"%s\")", aggFuncName, s.windowSize)

	newName := func(oldName string) string {
		return fmt.Sprintf(formatStr, oldName)
	}

	outputs := make([]models.Series, 0, len(series))
	for _, serie := range series {

		serie.Target = newName(serie.Target)
		serie.QueryPatt = newName(serie.QueryPatt)
		serie.Tags = serie.CopyTagsWith(aggFuncName, s.windowSize)
		serie.QueryFrom, serie.Datapoints = aggregateOnWindow(serie, aggFunc, s.shiftOffset, s.xFilesFactor)

		outputs = append(outputs, serie)
	}

	dataMap.Add(Req{}, outputs...)
	return outputs, nil
}

func aggregateOnWindow(serie models.Series, aggFunc batch.AggFunc, interval uint32, xFilesFactor float64) (uint32, []schema.Point) {
	out := pointSlicePool.Get().([]schema.Point)

	numPoints := len(serie.Datapoints)
	serieStart, serieEnd := serie.QueryFrom, serie.QueryTo
	queryStart := uint32(serieStart + interval)

	if queryStart < serieStart {
		panic("Query start is before the first point in series")
	}

	ptIdx := 0
	for ptIdx < numPoints-1 && serie.Datapoints[ptIdx+1].Ts <= queryStart {
		ptIdx++
	}

	for ptTs, stIdx := queryStart, 0; ptTs <= serieEnd && ptIdx < numPoints && stIdx <= ptIdx; stIdx++ {
		ptTs = serie.Datapoints[ptIdx].Ts

		points := serie.Datapoints[stIdx:ptIdx]
		aggPoint := schema.Point{Val: math.NaN(), Ts: ptTs}

		if pointsXffCheck(points, xFilesFactor) {
			aggPoint.Val = aggFunc(points)
		}

		ptIdx++
		out = append(out, aggPoint)
	}

	return queryStart, out
}

func (s *FuncMovingWindow) getWindowSeconds() (uint32, error) {

	// Discard the sign, if present, since we need to consider
	// preceding datapoints for each point on the graph
	durStr := s.windowSize
	if durStr[0] == '-' || durStr[0] == '+' {
		durStr = durStr[1:]
	}

	// Note: s.windowSize is not overwritten and will be appended as is to the
	// new Target, QueryPatt and Tags (keeping it consistent with graphite)
	// This implies a window size of '-2min' or '+2min' will be captured as is.
	// However the time operation is always a lookback over preceeding points.
	// If this needs to be corrected in the native implementation
	// uncomment the line below.
	// s.windowSize = durStr

	// Input validation and operations above check for empty or signed widowSizes
	// or for widowSize specified as points. Errors unlikely here
	shiftOffset, err := dur.ParseDuration(durStr)
	if err != nil {
		return 0, err
	}

	return shiftOffset, nil
}
<commit_msg>error logs and return values<commit_after>package expr

import (
	"errors"
	"fmt"
	"math"
	"strings"

	"github.com/grafana/metrictank/batch"
	"github.com/grafana/metrictank/schema"
	log "github.com/sirupsen/logrus"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/consolidation"
	"github.com/raintank/dur"
)

type FuncMovingWindow struct {
	in           GraphiteFunc
	windowSize   string
	fn           string
	xFilesFactor float64

	shiftOffset uint32
}

// NewMovingWindowConstructor takes an agg string and returns a constructor function
func NewMovingWindowConstructor(name string) func() GraphiteFunc {
	return func() GraphiteFunc {
		return &FuncMovingWindow{fn: name}
	}
}

func NewMovingWindow() GraphiteFunc {
	return &FuncMovingWindow{fn: "average", xFilesFactor: 0}
}

func (s *FuncMovingWindow) Signature() ([]Arg, []Arg) {
	return []Arg{
			ArgSeriesList{val: &s.in},
			ArgString{key: "windowSize", val: &s.windowSize, validator: []Validator{IsSignedIntervalString}},
			ArgString{key: "func", opt: true, val: &s.fn, validator: []Validator{IsConsolFunc}},
			ArgFloat{key: "xFilesFactor", opt: true, val: &s.xFilesFactor, validator: []Validator{WithinZeroOneInclusiveInterval}},
		}, []Arg{
			ArgSeriesList{},
		}
}

func (s *FuncMovingWindow) Context(context Context) Context {
	var err error
	s.shiftOffset, err = s.getWindowSeconds()
	if err != nil {
		// shouldn't happen, validated above
		log.Warnf("movingWindow: encountered error in getWindowSeconds, %s", err)
		return context
	}

	// Adjust to fetch from the shifted context.from with
	// context.to unchanged
	context.from -= s.shiftOffset
	return context
}

func (s *FuncMovingWindow) Exec(dataMap DataMap) ([]models.Series, error) {
	series, err := s.in.Exec(dataMap)
	if err != nil {
		return nil, err
	}

	aggFunc := consolidation.GetAggFunc(consolidation.FromConsolidateBy(s.fn))
	aggFuncName := fmt.Sprintf("moving%s", strings.Title(s.fn))
	formatStr := fmt.Sprintf("%s(%%s,\"%s\")", aggFuncName, s.windowSize)

	newName := func(oldName string) string {
		return fmt.Sprintf(formatStr, oldName)
	}

	outputs := make([]models.Series, 0, len(series))
	for _, serie := range series {

		from, points, err := aggregateOnWindow(serie, aggFunc, s.shiftOffset, s.xFilesFactor)
		if err != nil {
			return nil, err
		}

		serie.Target = newName(serie.Target)
		serie.QueryPatt = newName(serie.QueryPatt)
		serie.Tags = serie.CopyTagsWith(aggFuncName, s.windowSize)
		serie.QueryFrom = from
		serie.Datapoints = points

		outputs = append(outputs, serie)
	}

	dataMap.Add(Req{}, outputs...)
	return outputs, nil
}

func aggregateOnWindow(serie models.Series, aggFunc batch.AggFunc, interval uint32, xFilesFactor float64) (uint32, []schema.Point, error) {
	out := pointSlicePool.Get().([]schema.Point)

	numPoints := len(serie.Datapoints)
	serieStart, serieEnd := serie.QueryFrom, serie.QueryTo
	queryStart := uint32(serieStart + interval)

	if queryStart < serieStart {
		return 0, nil, errors.New("query start time cannot be before the first point in series")
	}

	ptIdx := 0
	for ptIdx < numPoints-1 && serie.Datapoints[ptIdx+1].Ts <= queryStart {
		ptIdx++
	}

	for ptTs, stIdx := queryStart, 0; ptTs <= serieEnd && ptIdx < numPoints && stIdx <= ptIdx; stIdx++ {
		ptTs = serie.Datapoints[ptIdx].Ts

		points := serie.Datapoints[stIdx:ptIdx]
		aggPoint := schema.Point{Val: math.NaN(), Ts: ptTs}

		if pointsXffCheck(points, xFilesFactor) {
			aggPoint.Val = aggFunc(points)
		}

		ptIdx++
		out = append(out, aggPoint)
	}

	return queryStart, out, nil
}

func (s *FuncMovingWindow) getWindowSeconds() (uint32, error) {

	// Discard the sign, if present, since we need to consider
	// preceding datapoints for each point on the graph
	durStr := s.windowSize
	if durStr[0] == '-' || durStr[0] == '+' {
		durStr = durStr[1:]
	}

	// Note: s.windowSize is not overwritten and will be appended as is to the
	// new Target, QueryPatt and Tags (keeping it consistent with graphite)
	// This implies a window size of '-2min' or '+2min' will be captured as is.
	// However the time operation is always a lookback over preceeding points.
	// If this needs to be corrected in the native implementation
	// uncomment the line below.
	// s.windowSize = durStr

	// Input validation and operations above check for empty or signed widowSizes
	// or for widowSize specified as points. Errors unlikely here
	shiftOffset, err := dur.ParseDuration(durStr)
	if err != nil {
		return 0, err
	}

	return shiftOffset, nil
}
<|endoftext|>


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

// +build unit

package facade_test

import (
	"time"

	"github.com/control-center/serviced/datastore"
	"github.com/control-center/serviced/domain/host"
	"github.com/control-center/serviced/domain/pool"
	"github.com/control-center/serviced/domain/service"
	"github.com/control-center/serviced/domain/serviceconfigfile"
	"github.com/control-center/serviced/utils"
	"github.com/stretchr/testify/mock"
	. "gopkg.in/check.v1"
)

type poolCacheEnv struct {
	resourcePool  pool.ResourcePool
	firstHost     host.Host
	secondHost    host.Host
	firstService  service.Service
	secondService service.Service
}

func SetupPoolCacheEnv() *poolCacheEnv {
	return &poolCacheEnv{
		resourcePool: pool.ResourcePool{
			ID:                "firstPool",
			Description:       "The first pool",
			MemoryCapacity:    0,
			MemoryCommitment:  0,
			CoreCapacity:      0,
			ConnectionTimeout: 10,
			CreatedAt:         time.Now(),
			UpdatedAt:         time.Now(),
			Permissions:       pool.DFSAccess,
		},
		firstHost: host.Host{
			ID:     "firstHost",
			PoolID: "firstPool",
			Cores:  6,
			Memory: 12000,
		},
		secondHost: host.Host{
			ID:     "secondHost",
			PoolID: "firstPool",
			Cores:  8,
			Memory: 10000,
		},
		firstService: service.Service{
			ID: "firstService",
			RAMCommitment: utils.EngNotation{
				Value: uint64(1000),
			},
		},
		secondService: service.Service{
			ID: "secondService",
			RAMCommitment: utils.EngNotation{
				Value: uint64(2000),
			},
		},
	}
}

// Test_PoolCacheEditService tests that the cache is invalidated when a
// service's ram commitment changes, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheEditService(c *C) {
	ft.setupMockDFSLocking()

	pc := SetupPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil)

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil)

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil).Once()

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	ft.serviceStore.On("Get", ft.ctx, pc.firstService.ID).
		Return(&pc.firstService, nil)

	ft.serviceStore.On("Put", ft.ctx, mock.AnythingOfType("*service.Service")).
		Return(nil)

	ft.configStore.On("GetConfigFiles", ft.ctx, mock.AnythingOfType("string"), mock.AnythingOfType("string")).
		Return([]*serviceconfigfile.SvcConfigFile{}, nil)

	ft.zzk.On("UpdateService", ft.ctx, mock.AnythingOfType("string"), mock.AnythingOfType("*service.Service"), false, false).
		Return(nil)

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 14)
	c.Assert(p.MemoryCapacity, Equals, uint64(22000))
	c.Assert(p.MemoryCommitment, Equals, uint64(3000))
	c.Assert(p.ConnectionTimeout, Equals, 10)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)

	pc.firstService.RAMCommitment = utils.EngNotation{
		Value: uint64(2000),
	}

	err = ft.Facade.UpdateService(ft.ctx, pc.firstService)
	c.Assert(err, IsNil)

	// Make sure that we return the new secondService with the updated RAMCommitment
	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil).Once()

	// GetReadPools should see that the cache is dirty, and update itself
	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 14)
	c.Assert(p.MemoryCapacity, Equals, uint64(22000))
	c.Assert(p.MemoryCommitment, Equals, uint64(4000))
	c.Assert(p.ConnectionTimeout, Equals, 10)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)
}

// Test_PoolCacheRemoveHost tests that cache is invalidated when a host is
// removed from a pool, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheRemoveHost(c *C) {
	ft.setupMockDFSLocking()

	pc := SetupPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil).Once()

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil)

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil)

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	ft.serviceStore.On("Get", ft.ctx, pc.firstService.ID).
		Return(&pc.firstService, nil)

	ft.serviceStore.On("Put", ft.ctx, mock.AnythingOfType("*service.Service")).
		Return(nil)

	ft.configStore.On("GetConfigFiles", ft.ctx, mock.AnythingOfType("string"), mock.AnythingOfType("string")).
		Return([]*serviceconfigfile.SvcConfigFile{}, nil)

	ft.hostStore.On("Get", ft.ctx, host.HostKey(pc.secondHost.ID), mock.AnythingOfType("*host.Host")).
		Return(nil).
		Run(func(args mock.Arguments) {
			*args.Get(2).(*host.Host) = pc.secondHost
		})

	ft.zzk.On("RemoveHost", &pc.secondHost).Return(nil)
	ft.zzk.On("UnregisterDfsClients", []host.Host{pc.secondHost}).Return(nil)

	ft.hostkeyStore.On("Delete", ft.ctx, pc.secondHost.ID).Return(nil)
	ft.hostStore.On("Delete", ft.ctx, host.HostKey(pc.secondHost.ID)).Return(nil)

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 14)
	c.Assert(p.MemoryCapacity, Equals, uint64(22000))
	c.Assert(p.MemoryCommitment, Equals, uint64(3000))
	c.Assert(p.ConnectionTimeout, Equals, 10)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)

	err = ft.Facade.RemoveHost(ft.ctx, pc.secondHost.ID)
	c.Assert(err, IsNil)

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost}, nil).Once()

	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 6)
	c.Assert(p.MemoryCapacity, Equals, uint64(12000))
	c.Assert(p.MemoryCommitment, Equals, uint64(3000))
	c.Assert(p.ConnectionTimeout, Equals, 10)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)
}

// Test_PoolCacheAddHost tests that cache is invalidated when a host is
// added to a pool, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheAddHost(c *C) {
	ft.setupMockDFSLocking()

	pc := SetupPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost}, nil).Once()

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil)

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil)

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	ft.serviceStore.On("Get", ft.ctx, pc.firstService.ID).
		Return(&pc.firstService, nil)

	ft.serviceStore.On("Put", ft.ctx, mock.AnythingOfType("*service.Service")).
		Return(nil)

	ft.configStore.On("GetConfigFiles", ft.ctx, mock.AnythingOfType("string"), mock.AnythingOfType("string")).
		Return([]*serviceconfigfile.SvcConfigFile{}, nil)

	ft.hostStore.On("Get", ft.ctx, host.HostKey(pc.secondHost.ID), mock.AnythingOfType("*host.Host")).
		Return(datastore.ErrNoSuchEntity{}).
		Once()

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 6)
	c.Assert(p.MemoryCapacity, Equals, uint64(12000))
	c.Assert(p.MemoryCommitment, Equals, uint64(3000))
	c.Assert(p.ConnectionTimeout, Equals, 10)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)

	ft.poolStore.On("Get", ft.ctx, pool.Key(pc.resourcePool.ID), mock.AnythingOfType("*pool.ResourcePool")).
		Return(nil).
		Run(func(args mock.Arguments) {
			*args.Get(2).(*pool.ResourcePool) = pc.resourcePool
		})

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil)

	ft.hostkeyStore.On("Put", ft.ctx, pc.secondHost.ID, mock.AnythingOfType("*hostkey.HostKey")).
		Return(nil)

	ft.hostStore.On("Put", ft.ctx, host.HostKey(pc.secondHost.ID), &pc.secondHost).
		Return(nil)

	ft.zzk.On("AddHost", &pc.secondHost).Return(nil)

	_, err = ft.Facade.AddHost(ft.ctx, &pc.secondHost)
	c.Assert(err, IsNil)

	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 14)
	c.Assert(p.MemoryCapacity, Equals, uint64(22000))
	c.Assert(p.MemoryCommitment, Equals, uint64(3000))
	c.Assert(p.ConnectionTimeout, Equals, 10)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)
}
<commit_msg>Remove extraneous tests. Add test for editing pool.<commit_after>// Copyright 2016 The Serviced 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.

// +build unit

package facade_test

import (
	"time"

	"github.com/control-center/serviced/datastore"
	"github.com/control-center/serviced/domain/host"
	"github.com/control-center/serviced/domain/pool"
	"github.com/control-center/serviced/domain/service"
	"github.com/control-center/serviced/domain/serviceconfigfile"
	"github.com/control-center/serviced/utils"
	"github.com/stretchr/testify/mock"
	. "gopkg.in/check.v1"
)

type poolCacheEnv struct {
	resourcePool  pool.ResourcePool
	firstHost     host.Host
	secondHost    host.Host
	firstService  service.Service
	secondService service.Service
}

func NewPoolCacheEnv() *poolCacheEnv {
	timeStamp := time.Now()
	return &poolCacheEnv{
		resourcePool: pool.ResourcePool{
			ID:                "firstPool",
			Description:       "The first pool",
			MemoryCapacity:    0,
			MemoryCommitment:  0,
			CoreCapacity:      0,
			ConnectionTimeout: 10,
			CreatedAt:         timeStamp,
			UpdatedAt:         timeStamp,
			Permissions:       pool.DFSAccess,
		},
		firstHost: host.Host{
			ID:     "firstHost",
			PoolID: "firstPool",
			Cores:  6,
			Memory: 12000,
		},
		secondHost: host.Host{
			ID:     "secondHost",
			PoolID: "firstPool",
			Cores:  8,
			Memory: 10000,
		},
		firstService: service.Service{
			ID: "firstService",
			RAMCommitment: utils.EngNotation{
				Value: uint64(1000),
			},
		},
		secondService: service.Service{
			ID: "secondService",
			RAMCommitment: utils.EngNotation{
				Value: uint64(2000),
			},
		},
	}
}

// Test_PoolCacheEditPool tests that the cache is invalidated when a
// Pool's permissions change, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheEditPool(c *C) {
	ft.setupMockDFSLocking()

	pc := NewPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil)

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil).Once()

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil)

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, TimeEqual, pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, pc.resourcePool.Permissions)

	pc.resourcePool.Permissions = pool.AdminAccess & pool.DFSAccess

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil).Once()

	ft.poolStore.On("Get", ft.ctx, pool.Key(pc.resourcePool.ID), mock.AnythingOfType("*pool.ResourcePool")).
		Return(nil).
		Run(func(args mock.Arguments) {
			*args.Get(2).(*pool.ResourcePool) = pc.resourcePool
		})

	ft.poolStore.On("Put", ft.ctx, pool.Key(pc.resourcePool.ID), mock.AnythingOfType("*pool.ResourcePool")).
		Return(nil)

	ft.zzk.On("UpdateResourcePool", mock.AnythingOfType("*pool.ResourcePool")).
		Return(nil)

	ft.Facade.UpdateResourcePool(ft.ctx, &pc.resourcePool)

	// GetReadPools should see that the cache is dirty, and update itself
	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CreatedAt, TimeEqual, pc.resourcePool.CreatedAt)
	c.Assert(p.UpdatedAt, Not(TimeEqual), pc.resourcePool.UpdatedAt)
	c.Assert(p.Permissions, Equals, (pool.AdminAccess & pool.DFSAccess))
}

// Test_PoolCacheEditService tests that the cache is invalidated when a
// service's ram commitment changes, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheEditService(c *C) {
	ft.setupMockDFSLocking()

	pc := NewPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil)

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil)

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil).Once()

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	ft.serviceStore.On("Get", ft.ctx, pc.firstService.ID).
		Return(&pc.firstService, nil)

	ft.serviceStore.On("Put", ft.ctx, mock.AnythingOfType("*service.Service")).
		Return(nil)

	ft.configStore.On("GetConfigFiles", ft.ctx, mock.AnythingOfType("string"), mock.AnythingOfType("string")).
		Return([]*serviceconfigfile.SvcConfigFile{}, nil)

	ft.zzk.On("UpdateService", ft.ctx, mock.AnythingOfType("string"), mock.AnythingOfType("*service.Service"), false, false).
		Return(nil)

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.MemoryCommitment, Equals, uint64(3000))

	pc.firstService.RAMCommitment = utils.EngNotation{
		Value: uint64(2000),
	}

	err = ft.Facade.UpdateService(ft.ctx, pc.firstService)
	c.Assert(err, IsNil)

	// Make sure that we return the new secondService with the updated RAMCommitment
	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil).Once()

	// GetReadPools should see that the cache is dirty, and update itself
	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.MemoryCommitment, Equals, uint64(4000))
}

// Test_PoolCacheRemoveHost tests that cache is invalidated when a host is
// removed from a pool, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheRemoveHost(c *C) {
	ft.setupMockDFSLocking()

	pc := NewPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil).Once()

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil)

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil)

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	ft.hostStore.On("Get", ft.ctx, host.HostKey(pc.secondHost.ID), mock.AnythingOfType("*host.Host")).
		Return(nil).
		Run(func(args mock.Arguments) {
			*args.Get(2).(*host.Host) = pc.secondHost
		})

	ft.zzk.On("RemoveHost", &pc.secondHost).Return(nil)
	ft.zzk.On("UnregisterDfsClients", []host.Host{pc.secondHost}).Return(nil)

	ft.hostkeyStore.On("Delete", ft.ctx, pc.secondHost.ID).Return(nil)
	ft.hostStore.On("Delete", ft.ctx, host.HostKey(pc.secondHost.ID)).Return(nil)

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 14)
	c.Assert(p.MemoryCapacity, Equals, uint64(22000))

	err = ft.Facade.RemoveHost(ft.ctx, pc.secondHost.ID)
	c.Assert(err, IsNil)

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost}, nil).Once()

	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 6)
	c.Assert(p.MemoryCapacity, Equals, uint64(12000))
}

// Test_PoolCacheAddHost tests that cache is invalidated when a host is
// added to a pool, and is subsequently updated on the next get
func (ft *FacadeUnitTest) Test_PoolCacheAddHost(c *C) {
	ft.setupMockDFSLocking()

	pc := NewPoolCacheEnv()

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost}, nil).Once()

	ft.poolStore.On("GetResourcePools", ft.ctx).
		Return([]pool.ResourcePool{pc.resourcePool}, nil)

	ft.serviceStore.On("GetServicesByPool", ft.ctx, pc.resourcePool.ID).
		Return([]service.Service{pc.firstService, pc.secondService}, nil)

	ft.serviceStore.On("GetServiceDetails", ft.ctx, pc.firstService.ID).
		Return(&service.ServiceDetails{
			ID:            pc.firstService.ID,
			RAMCommitment: pc.firstService.RAMCommitment,
		}, nil)

	ft.hostStore.On("Get", ft.ctx, host.HostKey(pc.secondHost.ID), mock.AnythingOfType("*host.Host")).
		Return(datastore.ErrNoSuchEntity{}).
		Once()

	pools, err := ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p := pools[0]

	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 6)
	c.Assert(p.MemoryCapacity, Equals, uint64(12000))

	ft.poolStore.On("Get", ft.ctx, pool.Key(pc.resourcePool.ID), mock.AnythingOfType("*pool.ResourcePool")).
		Return(nil).
		Run(func(args mock.Arguments) {
			*args.Get(2).(*pool.ResourcePool) = pc.resourcePool
		})

	ft.hostStore.On("FindHostsWithPoolID", ft.ctx, pc.resourcePool.ID).
		Return([]host.Host{pc.firstHost, pc.secondHost}, nil)

	ft.hostkeyStore.On("Put", ft.ctx, pc.secondHost.ID, mock.AnythingOfType("*hostkey.HostKey")).
		Return(nil)

	ft.hostStore.On("Put", ft.ctx, host.HostKey(pc.secondHost.ID), &pc.secondHost).
		Return(nil)

	ft.zzk.On("AddHost", &pc.secondHost).Return(nil)

	_, err = ft.Facade.AddHost(ft.ctx, &pc.secondHost)
	c.Assert(err, IsNil)

	pools, err = ft.Facade.GetReadPools(ft.ctx)
	c.Assert(err, IsNil)
	c.Assert(pools, Not(IsNil))
	c.Assert(len(pools), Equals, 1)

	p = pools[0]
	c.Assert(p.ID, Equals, pc.resourcePool.ID)
	c.Assert(p.CoreCapacity, Equals, 14)
	c.Assert(p.MemoryCapacity, Equals, uint64(22000))
}
<|endoftext|>