<commit_before>// Copyright (c) 2013-2017 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).

package mtx

//------------------------------------------------------------------------------

import (
	"fmt"

	micro "github.com/drakmaniso/glam/internal/microtext"
)

//------------------------------------------------------------------------------

// A Clip is used to output text to a part of the MTX screen.
//
// It implements the io.Writer interface.
type Clip struct {
	// Bounds in screen coordinates (i.e. 0,0 is the top left of the screen; -1,-1
	// is at bottom right). Both corners are included in the resulting rectangle.
	Left, Top     int
	Right, Bottom int

	// Cursor position, relative to top-left corner
	x, y int

	// Should characters be drawn highlighted?
	Highlighted bool

	// If true, the clip will be cleared with normal space characters
	// (translucents), otherwise with blanks (fully transparents).
	Solid bool
}

//------------------------------------------------------------------------------

// Clamp returns x and y clipped to the bounds of the Clip. Both the input and
// output are relative to the Clip bounds. If an input is negative, it is
// interpreted relative to the bottom right corner. The output is always
// positive.
func (cl *Clip) Clamp(x, y int) (int, int) {
	l, t, r, b := cl.Bounds()
	sx, sy := r-l, b-t

	if x < 0 {
		x += sx
	}
	if x < 0 {
		x = 0
	}
	if x > sx {
		x = sx
	}

	if y < 0 {
		y += sy
	}
	if y < 0 {
		y = 0
	}
	if y > sy {
		y = sy
	}

	return x, y
}

// Bounds returns the top left and bottom right corners of the Clip, in
// screen coordinates.
func (cl *Clip) Bounds() (left, top int, right, bottom int) {
	left, top = Clamp(cl.Left, cl.Top)
	right, bottom = Clamp(cl.Right, cl.Bottom)
	if cl.Right == 0 && cl.Bottom == 0 && cl.Left == 0 && cl.Top == 0 {
		right, bottom = micro.Size()
		right--
		bottom--
	}
	return left, top, right, bottom
}

//------------------------------------------------------------------------------

// Locate positions the Clip cursor, in coordinates relative to its bounds.
// Positive coordinates are interpreted from the top left corner, while negative
// coordinates are interpreted from the bottom-right corner.
func (cl *Clip) Locate(x, y int) {
	cl.x, cl.y = cl.Clamp(x, y)
}

//------------------------------------------------------------------------------

// Clear erases all text from the clip.
//
// Note: you can customize the character used to erase by setting the ClearChar
// field of the clip.
func (cl *Clip) Clear() {
	l, t, r, b := cl.Bounds()
	cl.x, cl.y = 0, 0

	clr := byte('\x00')
	if cl.Solid {
		clr = ' '
	}

	for y := t; y <= b; y++ {
		for x := l; x <= r; x++ {
			micro.Poke(x, y, clr)
		}
	}
}

//------------------------------------------------------------------------------

// Write outputs text to the clip, starting at the cursor position. Special
// characters such as newline and tabs are recognized. It always returns the
// total number of bytes in the slice, even if some characters are out-of-bounds
// and clipped.
//
// Special characters:
// - '\a' toggle highlight
// - '\b' move cursor one character left
// - '\f' transparent space
// - '\n' newline
// - '\r' move cursor to beginning of line
// - '\t' tabulation
// - '\v' clear until end of line
func (cl *Clip) Write(p []byte) (n int, err error) {
	l, t, r, b := cl.Bounds()
	sx, sy := r-l, b-t

	colour := byte(0x00)
	// Prepare reverse video mask
	if cl.Highlighted {
		colour = byte(0x80)
	}

	clr := byte('\x00')
	if cl.Solid {
		clr = ' '
	}

	x, y := cl.Clamp(cl.x, cl.y)

	for _, c := range p {
		switch {
		case ' ' <= c && c <= '~':
			c |= colour

		case c == '\n':
			x = 0
			if y == sy {
				cl.Scroll(0, -1)
			} else {
				y++
			}
			continue

		case c == '\r':
			x = 0
			continue

		case c == '\f':
			c = '\x00'

		case c == '\v':
			if 0 <= y && y <= sy {
				i := x
				if i < 0 {
					i = 0
				}
				for ; i <= sx; i++ {
					micro.Poke(l+i, t+y, clr)
				}
			}
			continue

		case c == '\t':
			if 0 <= y && y <= sy {
				n := ((x/8)+1)*8 - x
				for i := 0; i < n && x+i <= sy; i++ {
					micro.Poke(l+x+i, t+y, ' ')
				}
				x += n
			}
			continue

		case c == '\b':
			x--
			continue

		case c == '\a':
			if colour != 0 {
				colour = 0
			} else {
				colour = 0x80
			}
			continue

		default:
			c = '\x7F' | colour
		}

		// Handle out of bounds
		var xx, yy int

		if x < 0 {
			c = '~' + 1
			xx = l
		} else if x > sx {
			c = '~' + 1
			xx = r
		} else {
			xx = l + x
		}

		if y < 0 {
			c = '~' + 1
			yy = t
		} else if y > sy {
			c = '~' + 1
			yy = b
		} else {
			yy = t + y
		}

		micro.Poke(xx, yy, c)

		// Either scroll horizontally or move cursor
		if x == sx {
			x = 0
			if y == sy {
				cl.Scroll(0, -1)
			} else {
				y++
			}
		} else {
			x++
		}
	}

	cl.x, cl.y = x, y

	return len(p), nil
}

//------------------------------------------------------------------------------

// Print writes formatted text on the clip. It is equivalent to a call to
// fmt.Fprintf on the clip.
func (cl *Clip) Print(format string, a ...interface{}) {
	fmt.Fprintf(cl, format, a...)
}

//------------------------------------------------------------------------------

// Scroll moves the content of the clip by a specified amount. Everything that
// move out of bounds is discarded, and the liberated space is cleared.
func (cl *Clip) Scroll(dx, dy int) {
	l, t, r, b := cl.Bounds()

	clr := byte('\x00')
	if cl.Solid {
		clr = ' '
	}

	var x1, x2, x3, incX, y1, y2, y3, incY int
	var cmpX, cmpY func(int, int) bool
	if dx >= 0 {
		x1 = r
		if dx > r-l {
			dx = r - l + 1
		}
		x2 = l + dx
		x3 = l
		incX = -1
		cmpX = func(a, b int) bool { return a >= b }
	} else {
		x1 = l
		if dx < l-r {
			dx = l - r - 1
		}
		x2 = r + dx
		x3 = r
		incX = +1
		cmpX = func(a, b int) bool { return a <= b }
	}
	if dy >= 0 {
		y1 = b
		if dy > b-t {
			dy = b - t + 1
		}
		y2 = t + dy
		y3 = t
		incY = -1
		cmpY = func(a, b int) bool { return a >= b }
	} else {
		y1 = t
		if dy < t-b {
			dy = t - b - 1
		}
		y2 = b + dy
		y3 = b
		incY = +1
		cmpY = func(a, b int) bool { return a <= b }
	}

	for y := y1; cmpY(y, y2); y += incY {
		for x := x1; cmpX(x, x2); x += incX {
			micro.Poke(x, y, micro.Peek(x-dx, y-dy))
		}
		for x := x2 + incX; cmpX(x, x3); x += incX {
			micro.Poke(x, y, clr)
		}
	}
	for y := y2 + incY; cmpY(y, y3); y += incY {
		for x := l; x <= r; x++ {
			micro.Poke(x, y, clr)
		}
	}
}

//------------------------------------------------------------------------------
<commit_msg>Improve mtx<commit_after>// Copyright (c) 2013-2017 Laurent Moussault. All rights reserved.
// Licensed under a simplified BSD license (see LICENSE file).

package mtx

//------------------------------------------------------------------------------

import (
	"fmt"

	micro "github.com/drakmaniso/glam/internal/microtext"
)

//------------------------------------------------------------------------------

// A Clip is used to output text to a part of the MTX screen.
//
// It implements the io.Writer interface.
type Clip struct {
	// Bounds in screen coordinates (i.e. 0,0 is the top left of the screen; -1,-1
	// is at bottom right). Both corners are included in the resulting rectangle.
	Left, Top     int
	Right, Bottom int

	// Cursor position, relative to top-left corner
	x, y int

	// Should characters be drawn highlighted?
	Highlighted bool

	// If true, the clip will be cleared with normal space characters
	// (translucents), otherwise with blanks (fully transparents).
	Solid bool
}

//------------------------------------------------------------------------------

// Clamp returns x and y clipped to the bounds of the Clip. Both the input and
// output are relative to the Clip bounds. If an input is negative, it is
// interpreted relative to the bottom right corner. The output is always
// positive.
func (cl *Clip) Clamp(x, y int) (int, int) {
	l, t, r, b := cl.Bounds()
	sx, sy := r-l, b-t

	if x < 0 {
		x += sx
	}
	if x < 0 {
		x = 0
	}
	if x > sx {
		x = sx
	}

	if y < 0 {
		y += sy
	}
	if y < 0 {
		y = 0
	}
	if y > sy {
		y = sy
	}

	return x, y
}

// Bounds returns the top left and bottom right corners of the Clip, in
// screen coordinates.
func (cl *Clip) Bounds() (left, top int, right, bottom int) {
	left, top = Clamp(cl.Left, cl.Top)
	right, bottom = Clamp(cl.Right, cl.Bottom)
	if cl.Right == 0 && cl.Bottom == 0 && cl.Left == 0 && cl.Top == 0 {
		right, bottom = micro.Size()
		right--
		bottom--
	}
	return left, top, right, bottom
}

//------------------------------------------------------------------------------

// Locate positions the Clip cursor, in coordinates relative to its bounds.
// Positive coordinates are interpreted from the top left corner, while negative
// coordinates are interpreted from the bottom-right corner.
func (cl *Clip) Locate(x, y int) {
	cl.x, cl.y = cl.Clamp(x, y)
}

//------------------------------------------------------------------------------

// Clear erases all text from the clip.
//
// Note: you can customize the character used to erase by setting the ClearChar
// field of the clip.
func (cl *Clip) Clear() {
	l, t, r, b := cl.Bounds()
	cl.x, cl.y = 0, 0

	clr := byte('\x00')
	if cl.Solid {
		clr = ' '
	}

	for y := t; y <= b; y++ {
		for x := l; x <= r; x++ {
			micro.Poke(x, y, clr)
		}
	}
}

//------------------------------------------------------------------------------

// Write outputs text to the clip, starting at the cursor position. Special
// characters such as newline and tabs are recognized. It always returns the
// total number of bytes in the slice, even if some characters are out-of-bounds
// and clipped.
//
// Special characters:
// - '~' blank space (i.e. fully transparent)
// - '\a' toggle highlight
// - '\b' move cursor one character left
// - '\f' escaped ~
// - '\n' newline
// - '\r' move cursor to beginning of line
// - '\t' tabulation
// - '\v' clear until end of line
func (cl *Clip) Write(p []byte) (n int, err error) {
	l, t, r, b := cl.Bounds()
	sx, sy := r-l, b-t

	colour := byte(0x00)
	// Prepare reverse video mask
	if cl.Highlighted {
		colour = byte(0x80)
	}

	clr := byte('\x00')
	if cl.Solid {
		clr = ' '
	}

	x, y := cl.Clamp(cl.x, cl.y)

	for _, c := range p {
		switch {
		case ' ' <= c && c <= '}':
			c |= colour

		case c == '\r':
			x = 0
			continue

		case c == '\f':
			c = '~'
		case c == '~':
			c = '\x00'

		case c == '\n':
			// First, clear to end of line
			if 0 <= y && y <= sy {
				i := x
				if i < 0 {
					i = 0
				}
				for ; i <= sx; i++ {
					micro.Poke(l+i, t+y, clr)
				}
			}
			// Go to next line
			x = 0
			if y == sy {
				cl.Scroll(0, -1)
			} else {
				y++
			}
			continue
		case c == '\v':
			// Clear to end of line
			if 0 <= y && y <= sy {
				i := x
				if i < 0 {
					i = 0
				}
				for ; i <= sx; i++ {
					micro.Poke(l+i, t+y, clr)
				}
			}
			continue

		case c == '\t':
			if 0 <= y && y <= sy {
				n := ((x/8)+1)*8 - x
				for i := 0; i < n && x+i <= sy; i++ {
					micro.Poke(l+x+i, t+y, ' ')
				}
				x += n
			}
			continue

		case c == '\b':
			x--
			continue

		case c == '\a':
			if colour != 0 {
				colour = 0
			} else {
				colour = 0x80
			}
			continue

		default:
			c = '\x7F' | colour
		}

		// Handle out of bounds
		var xx, yy int

		if x < 0 {
			c = '~' + 1
			xx = l
		} else if x > sx {
			c = '~' + 1
			xx = r
		} else {
			xx = l + x
		}

		if y < 0 {
			c = '~' + 1
			yy = t
		} else if y > sy {
			c = '~' + 1
			yy = b
		} else {
			yy = t + y
		}

		micro.Poke(xx, yy, c)

		// Either scroll horizontally or move cursor
		if x == sx {
			x = 0
			if y == sy {
				cl.Scroll(0, -1)
			} else {
				y++
			}
		} else {
			x++
		}
	}

	cl.x, cl.y = x, y

	return len(p), nil
}

//------------------------------------------------------------------------------

// Print writes formatted text on the clip. It is equivalent to a call to
// fmt.Fprintf on the clip.
func (cl *Clip) Print(format string, a ...interface{}) {
	fmt.Fprintf(cl, format, a...)
}

//------------------------------------------------------------------------------

// Scroll moves the content of the clip by a specified amount. Everything that
// move out of bounds is discarded, and the liberated space is cleared.
func (cl *Clip) Scroll(dx, dy int) {
	l, t, r, b := cl.Bounds()

	clr := byte('\x00')
	if cl.Solid {
		clr = ' '
	}

	var x1, x2, x3, incX, y1, y2, y3, incY int
	var cmpX, cmpY func(int, int) bool
	if dx >= 0 {
		x1 = r
		if dx > r-l {
			dx = r - l + 1
		}
		x2 = l + dx
		x3 = l
		incX = -1
		cmpX = func(a, b int) bool { return a >= b }
	} else {
		x1 = l
		if dx < l-r {
			dx = l - r - 1
		}
		x2 = r + dx
		x3 = r
		incX = +1
		cmpX = func(a, b int) bool { return a <= b }
	}
	if dy >= 0 {
		y1 = b
		if dy > b-t {
			dy = b - t + 1
		}
		y2 = t + dy
		y3 = t
		incY = -1
		cmpY = func(a, b int) bool { return a >= b }
	} else {
		y1 = t
		if dy < t-b {
			dy = t - b - 1
		}
		y2 = b + dy
		y3 = b
		incY = +1
		cmpY = func(a, b int) bool { return a <= b }
	}

	for y := y1; cmpY(y, y2); y += incY {
		for x := x1; cmpX(x, x2); x += incX {
			micro.Poke(x, y, micro.Peek(x-dx, y-dy))
		}
		for x := x2 + incX; cmpX(x, x3); x += incX {
			micro.Poke(x, y, clr)
		}
	}
	for y := y2 + incY; cmpY(y, y3); y += incY {
		for x := l; x <= r; x++ {
			micro.Poke(x, y, clr)
		}
	}
}

//------------------------------------------------------------------------------
<|endoftext|>


<commit_before>package mustache

import (
    "bytes"
    "container/vector"
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "path"
    "reflect"
    "strings"
)

type textElement struct {
    text []byte
}

type varElement struct {
    name string
}

type sectionElement struct {
    name      string
    startline int
    elems     *vector.Vector
}

type template struct {
    data    string
    otag    string
    ctag    string
    p       int
    curline int
    dir     string
    elems   *vector.Vector
}

type parseError struct {
    line    int
    message string
}

func (p parseError) String() string { return fmt.Sprintf("line %d: %s", p.line, p.message) }

func (tmpl *template) readString(s string) (string, os.Error) {
    i := tmpl.p
    newlines := 0
    for true {
        //are we at the end of the string?
        if i+len(s) > len(tmpl.data) {
            return tmpl.data[tmpl.p:], os.EOF
        }

        if tmpl.data[i] == '\n' {
            newlines++
        }

        if tmpl.data[i] != s[0] {
            i++
            continue
        }

        match := true
        for j := 1; j < len(s); j++ {
            if s[j] != tmpl.data[i+j] {
                match = false
                break
            }
        }

        if match {
            e := i + len(s)
            text := tmpl.data[tmpl.p:e]
            tmpl.p = e

            tmpl.curline += newlines
            return text, nil
        } else {
            i++
        }
    }

    //should never be here
    return "", nil
}

func (tmpl *template) parsePartial(name string) (*template, os.Error) {
    filename := path.Join(tmpl.dir, name+".mustache")

    partial, err := ParseFile(filename)

    if err != nil {
        return nil, err
    }

    return partial, nil
}

func (tmpl *template) parseSection(section *sectionElement) os.Error {
    for {
        text, err := tmpl.readString(tmpl.otag)

        if err == os.EOF {
            return parseError{section.startline, "Section " + section.name + " has no closing tag"}
        }

        // put text into an item
        text = text[0 : len(text)-len(tmpl.otag)]
        section.elems.Push(&textElement{[]byte(text)})

        text, err = tmpl.readString(tmpl.ctag)
        if err == os.EOF {
            //put the remaining text in a block
            return parseError{tmpl.curline, "unmatched open tag"}
        }

        //trim the close tag off the text
        tag := strings.TrimSpace(text[0 : len(text)-len(tmpl.ctag)])
        if len(tag) == 0 {
            return parseError{tmpl.curline, "empty tag"}
        }
        switch tag[0] {
        case '!':
            //ignore comment
            break
        case '#':
            name := strings.TrimSpace(tag[1:])

            //ignore the newline when a section starts
            if len(tmpl.data) > tmpl.p && tmpl.data[tmpl.p] == '\n' {
                tmpl.p += 1
            } else if len(tmpl.data) > tmpl.p+1 && tmpl.data[tmpl.p] == '\r' && tmpl.data[tmpl.p+1] == '\n' {
                tmpl.p += 2
            }

            se := sectionElement{name, tmpl.curline, new(vector.Vector)}
            err := tmpl.parseSection(&se)
            if err != nil {
                return err
            }
            section.elems.Push(&se)
        case '/':
            name := strings.TrimSpace(tag[1:])
            if name != section.name {
                return parseError{tmpl.curline, "interleaved closing tag: " + name}
            } else {
                return nil
            }
        case '>':
            name := strings.TrimSpace(tag[1:])
            partial, err := tmpl.parsePartial(name)
            if err != nil {
                return err
            }
            tmpl.elems.Push(partial)
        case '=':
            if tag[len(tag)-1] != '=' {
                return parseError{tmpl.curline, "Invalid meta tag"}
            }
            tag = strings.TrimSpace(tag[1 : len(tag)-1])
            newtags := strings.Split(tag, " ", 0)
            if len(newtags) == 2 {
                tmpl.otag = newtags[0]
                tmpl.ctag = newtags[1]
            }
        default:
            section.elems.Push(&varElement{tag})
        }
    }

    return nil
}

func (tmpl *template) parse() os.Error {
    for {
        text, err := tmpl.readString(tmpl.otag)

        if err == os.EOF {
            //put the remaining text in a block
            tmpl.elems.Push(&textElement{[]byte(text)})
            return nil
        }

        // put text into an item
        text = text[0 : len(text)-len(tmpl.otag)]
        tmpl.elems.Push(&textElement{[]byte(text)})

        text, err = tmpl.readString(tmpl.ctag)
        if err == os.EOF {
            //put the remaining text in a block
            return parseError{tmpl.curline, "unmatched open tag"}
        }

        //trim the close tag off the text
        tag := strings.TrimSpace(text[0 : len(text)-len(tmpl.ctag)])
        if len(tag) == 0 {
            return parseError{tmpl.curline, "empty tag"}
        }
        switch tag[0] {
        case '!':
            //ignore comment
            break
        case '#':
            name := strings.TrimSpace(tag[1:])

            if len(tmpl.data) > tmpl.p && tmpl.data[tmpl.p] == '\n' {
                tmpl.p += 1
            } else if len(tmpl.data) > tmpl.p+1 && tmpl.data[tmpl.p] == '\r' && tmpl.data[tmpl.p+1] == '\n' {
                tmpl.p += 2
            }

            se := sectionElement{name, tmpl.curline, new(vector.Vector)}
            err := tmpl.parseSection(&se)
            if err != nil {
                return err
            }
            tmpl.elems.Push(&se)
        case '/':
            return parseError{tmpl.curline, "unmatched close tag"}
        case '>':
            name := strings.TrimSpace(tag[1:])
            partial, err := tmpl.parsePartial(name)
            if err != nil {
                return err
            }
            tmpl.elems.Push(partial)
        case '=':
            if tag[len(tag)-1] != '=' {
                return parseError{tmpl.curline, "Invalid meta tag"}
            }
            tag = strings.TrimSpace(tag[1 : len(tag)-1])
            newtags := strings.Split(tag, " ", 0)
            if len(newtags) == 2 {
                tmpl.otag = newtags[0]
                tmpl.ctag = newtags[1]
            }
        default:
            tmpl.elems.Push(&varElement{tag})
        }
    }

    return nil
}

// See if name is a method of the value at some level of indirection.
// The return values are the result of the call (which may be nil if
// there's trouble) and whether a method of the right name exists with
// any signature.
func callMethod(data reflect.Value, name string) (result reflect.Value, found bool) {
    found = false
    // Method set depends on pointerness, and the value may be arbitrarily
    // indirect.  Simplest approach is to walk down the pointer chain and
    // see if we can find the method at each step.
    // Most steps will see NumMethod() == 0.
    for {
        typ := data.Type()
        if nMethod := data.Type().NumMethod(); nMethod > 0 {
            for i := 0; i < nMethod; i++ {
                method := typ.Method(i)
                if method.Name == name {
                    found = true // we found the name regardless
                    // does receiver type match? (pointerness might be off)
                    if typ == method.Type.In(0) {
                        return call(data, method), found
                    }
                }
            }
        }
        if nd, ok := data.(*reflect.PtrValue); ok {
            data = nd.Elem()
        } else {
            break
        }
    }
    return
}

// Invoke the method. If its signature is wrong, return nil.
func call(v reflect.Value, method reflect.Method) reflect.Value {
    funcType := method.Type
    // Method must take no arguments, meaning as a func it has one argument (the receiver)
    if funcType.NumIn() != 1 {
        return nil
    }
    // Method must return a single value.
    if funcType.NumOut() != 1 {
        return nil
    }
    // Result will be the zeroth element of the returned slice.
    return method.Func.Call([]reflect.Value{v})[0]
}

func lookup(context reflect.Value, name string) reflect.Value {
    //if the context is an interface, get the actual value
    if iface, ok := context.(*reflect.InterfaceValue); ok && !iface.IsNil() {
        context = iface.Elem()
    }

    //the context may be a pointer, so do an indirect
    contextInd := reflect.Indirect(context)

    var ret reflect.Value = nil

    switch val := contextInd.(type) {
    case *reflect.MapValue:
        ret = val.Elem(reflect.NewValue(name))
    case *reflect.StructValue:
        //look for a field
        ret = val.FieldByName(name)
    }

    //look for a method
    if ret == nil {
        if result, found := callMethod(context, name); found {
            ret = result
        }
    }

    //if the lookup value is an interface, return the actual value
    if iface, ok := ret.(*reflect.InterfaceValue); ok && !iface.IsNil() {
        ret = iface.Elem()
    }

    return ret
}

func renderSection(section *sectionElement, context reflect.Value, buf io.Writer) {
    value := lookup(context, section.name)

    if value.Interface() == nil {
        return
    }

    valueInd := reflect.Indirect(value)
    //if the section is nil, we shouldn't do anything

    var contexts = new(vector.Vector)

    switch val := valueInd.(type) {
    case *reflect.BoolValue:
        if !val.Get() {
            return
        } else {
            contexts.Push(context)
        }
    case *reflect.SliceValue:
        for i := 0; i < val.Len(); i++ {
            contexts.Push(val.Elem(i))
        }
    case *reflect.ArrayValue:
        for i := 0; i < val.Len(); i++ {
            contexts.Push(val.Elem(i))
        }
    default:
        contexts.Push(context)
    }

    //by default we execute the section
    for j := 0; j < contexts.Len(); j++ {
        ctx := contexts.At(j).(reflect.Value)
        for i := 0; i < section.elems.Len(); i++ {
            renderElement(section.elems.At(i), ctx, buf)
        }
    }
}

func renderElement(element interface{}, context reflect.Value, buf io.Writer) {

    switch elem := element.(type) {
    case *textElement:
        buf.Write(elem.text)
    case *varElement:
        val := lookup(context, elem.name)
        if val != nil {
            fmt.Fprint(buf, val.Interface())
        }
    case *sectionElement:
        renderSection(elem, context, buf)
    case *template:
        elem.renderTemplate(context, buf)
    }
}

func (tmpl *template) renderTemplate(context reflect.Value, buf io.Writer) {
    for i := 0; i < tmpl.elems.Len(); i++ {
        renderElement(tmpl.elems.At(i), context, buf)
    }
}

func (tmpl *template) Render(context interface{}, buf io.Writer) {
    val := reflect.NewValue(context)
    tmpl.renderTemplate(val, buf)
}

func ParseString(data string) (*template, os.Error) {
    cwd := os.Getenv("CWD")
    tmpl := template{data, "{{", "}}", 0, 1, cwd, new(vector.Vector)}
    err := tmpl.parse()

    if err != nil {
        return nil, err
    }

    return &tmpl, err
}

func ParseFile(filename string) (*template, os.Error) {
    data, err := ioutil.ReadFile(filename)

    if err != nil {
        return nil, err
    }

    dirname, _ := path.Split(filename)

    tmpl := template{string(data), "{{", "}}", 0, 1, dirname, new(vector.Vector)}
    err = tmpl.parse()

    if err != nil {
        return nil, err
    }

    return &tmpl, nil
}

func Render(data string, context interface{}) (string, os.Error) {
    tmpl, err := ParseString(data)

    if err != nil {
        return "", err
    }

    var buf bytes.Buffer
    tmpl.Render(context, &buf)

    return buf.String(), nil
}

func RenderFile(filename string, context interface{}) (string, os.Error) {
    tmpl, err := ParseFile(filename)

    if err != nil {
        return "", err
    }

    var buf bytes.Buffer
    tmpl.Render(context, &buf)

    return buf.String(), nil
}
<commit_msg>Made template type public, it's now Template.<commit_after>package mustache

import (
    "bytes"
    "container/vector"
    "fmt"
    "io"
    "io/ioutil"
    "os"
    "path"
    "reflect"
    "strings"
)

type textElement struct {
    text []byte
}

type varElement struct {
    name string
}

type sectionElement struct {
    name      string
    startline int
    elems     *vector.Vector
}

type Template struct {
    data    string
    otag    string
    ctag    string
    p       int
    curline int
    dir     string
    elems   *vector.Vector
}

type parseError struct {
    line    int
    message string
}

func (p parseError) String() string { return fmt.Sprintf("line %d: %s", p.line, p.message) }

func (tmpl *Template) readString(s string) (string, os.Error) {
    i := tmpl.p
    newlines := 0
    for true {
        //are we at the end of the string?
        if i+len(s) > len(tmpl.data) {
            return tmpl.data[tmpl.p:], os.EOF
        }

        if tmpl.data[i] == '\n' {
            newlines++
        }

        if tmpl.data[i] != s[0] {
            i++
            continue
        }

        match := true
        for j := 1; j < len(s); j++ {
            if s[j] != tmpl.data[i+j] {
                match = false
                break
            }
        }

        if match {
            e := i + len(s)
            text := tmpl.data[tmpl.p:e]
            tmpl.p = e

            tmpl.curline += newlines
            return text, nil
        } else {
            i++
        }
    }

    //should never be here
    return "", nil
}

func (tmpl *Template) parsePartial(name string) (*Template, os.Error) {
    filename := path.Join(tmpl.dir, name+".mustache")

    partial, err := ParseFile(filename)

    if err != nil {
        return nil, err
    }

    return partial, nil
}

func (tmpl *Template) parseSection(section *sectionElement) os.Error {
    for {
        text, err := tmpl.readString(tmpl.otag)

        if err == os.EOF {
            return parseError{section.startline, "Section " + section.name + " has no closing tag"}
        }

        // put text into an item
        text = text[0 : len(text)-len(tmpl.otag)]
        section.elems.Push(&textElement{[]byte(text)})

        text, err = tmpl.readString(tmpl.ctag)
        if err == os.EOF {
            //put the remaining text in a block
            return parseError{tmpl.curline, "unmatched open tag"}
        }

        //trim the close tag off the text
        tag := strings.TrimSpace(text[0 : len(text)-len(tmpl.ctag)])
        if len(tag) == 0 {
            return parseError{tmpl.curline, "empty tag"}
        }
        switch tag[0] {
        case '!':
            //ignore comment
            break
        case '#':
            name := strings.TrimSpace(tag[1:])

            //ignore the newline when a section starts
            if len(tmpl.data) > tmpl.p && tmpl.data[tmpl.p] == '\n' {
                tmpl.p += 1
            } else if len(tmpl.data) > tmpl.p+1 && tmpl.data[tmpl.p] == '\r' && tmpl.data[tmpl.p+1] == '\n' {
                tmpl.p += 2
            }

            se := sectionElement{name, tmpl.curline, new(vector.Vector)}
            err := tmpl.parseSection(&se)
            if err != nil {
                return err
            }
            section.elems.Push(&se)
        case '/':
            name := strings.TrimSpace(tag[1:])
            if name != section.name {
                return parseError{tmpl.curline, "interleaved closing tag: " + name}
            } else {
                return nil
            }
        case '>':
            name := strings.TrimSpace(tag[1:])
            partial, err := tmpl.parsePartial(name)
            if err != nil {
                return err
            }
            tmpl.elems.Push(partial)
        case '=':
            if tag[len(tag)-1] != '=' {
                return parseError{tmpl.curline, "Invalid meta tag"}
            }
            tag = strings.TrimSpace(tag[1 : len(tag)-1])
            newtags := strings.Split(tag, " ", 0)
            if len(newtags) == 2 {
                tmpl.otag = newtags[0]
                tmpl.ctag = newtags[1]
            }
        default:
            section.elems.Push(&varElement{tag})
        }
    }

    return nil
}

func (tmpl *Template) parse() os.Error {
    for {
        text, err := tmpl.readString(tmpl.otag)

        if err == os.EOF {
            //put the remaining text in a block
            tmpl.elems.Push(&textElement{[]byte(text)})
            return nil
        }

        // put text into an item
        text = text[0 : len(text)-len(tmpl.otag)]
        tmpl.elems.Push(&textElement{[]byte(text)})

        text, err = tmpl.readString(tmpl.ctag)
        if err == os.EOF {
            //put the remaining text in a block
            return parseError{tmpl.curline, "unmatched open tag"}
        }

        //trim the close tag off the text
        tag := strings.TrimSpace(text[0 : len(text)-len(tmpl.ctag)])
        if len(tag) == 0 {
            return parseError{tmpl.curline, "empty tag"}
        }
        switch tag[0] {
        case '!':
            //ignore comment
            break
        case '#':
            name := strings.TrimSpace(tag[1:])

            if len(tmpl.data) > tmpl.p && tmpl.data[tmpl.p] == '\n' {
                tmpl.p += 1
            } else if len(tmpl.data) > tmpl.p+1 && tmpl.data[tmpl.p] == '\r' && tmpl.data[tmpl.p+1] == '\n' {
                tmpl.p += 2
            }

            se := sectionElement{name, tmpl.curline, new(vector.Vector)}
            err := tmpl.parseSection(&se)
            if err != nil {
                return err
            }
            tmpl.elems.Push(&se)
        case '/':
            return parseError{tmpl.curline, "unmatched close tag"}
        case '>':
            name := strings.TrimSpace(tag[1:])
            partial, err := tmpl.parsePartial(name)
            if err != nil {
                return err
            }
            tmpl.elems.Push(partial)
        case '=':
            if tag[len(tag)-1] != '=' {
                return parseError{tmpl.curline, "Invalid meta tag"}
            }
            tag = strings.TrimSpace(tag[1 : len(tag)-1])
            newtags := strings.Split(tag, " ", 0)
            if len(newtags) == 2 {
                tmpl.otag = newtags[0]
                tmpl.ctag = newtags[1]
            }
        default:
            tmpl.elems.Push(&varElement{tag})
        }
    }

    return nil
}

// See if name is a method of the value at some level of indirection.
// The return values are the result of the call (which may be nil if
// there's trouble) and whether a method of the right name exists with
// any signature.
func callMethod(data reflect.Value, name string) (result reflect.Value, found bool) {
    found = false
    // Method set depends on pointerness, and the value may be arbitrarily
    // indirect.  Simplest approach is to walk down the pointer chain and
    // see if we can find the method at each step.
    // Most steps will see NumMethod() == 0.
    for {
        typ := data.Type()
        if nMethod := data.Type().NumMethod(); nMethod > 0 {
            for i := 0; i < nMethod; i++ {
                method := typ.Method(i)
                if method.Name == name {
                    found = true // we found the name regardless
                    // does receiver type match? (pointerness might be off)
                    if typ == method.Type.In(0) {
                        return call(data, method), found
                    }
                }
            }
        }
        if nd, ok := data.(*reflect.PtrValue); ok {
            data = nd.Elem()
        } else {
            break
        }
    }
    return
}

// Invoke the method. If its signature is wrong, return nil.
func call(v reflect.Value, method reflect.Method) reflect.Value {
    funcType := method.Type
    // Method must take no arguments, meaning as a func it has one argument (the receiver)
    if funcType.NumIn() != 1 {
        return nil
    }
    // Method must return a single value.
    if funcType.NumOut() != 1 {
        return nil
    }
    // Result will be the zeroth element of the returned slice.
    return method.Func.Call([]reflect.Value{v})[0]
}

func lookup(context reflect.Value, name string) reflect.Value {
    //if the context is an interface, get the actual value
    if iface, ok := context.(*reflect.InterfaceValue); ok && !iface.IsNil() {
        context = iface.Elem()
    }

    //the context may be a pointer, so do an indirect
    contextInd := reflect.Indirect(context)

    var ret reflect.Value = nil

    switch val := contextInd.(type) {
    case *reflect.MapValue:
        ret = val.Elem(reflect.NewValue(name))
    case *reflect.StructValue:
        //look for a field
        ret = val.FieldByName(name)
    }

    //look for a method
    if ret == nil {
        if result, found := callMethod(context, name); found {
            ret = result
        }
    }

    //if the lookup value is an interface, return the actual value
    if iface, ok := ret.(*reflect.InterfaceValue); ok && !iface.IsNil() {
        ret = iface.Elem()
    }

    return ret
}

func renderSection(section *sectionElement, context reflect.Value, buf io.Writer) {
    value := lookup(context, section.name)

    if value.Interface() == nil {
        return
    }

    valueInd := reflect.Indirect(value)
    //if the section is nil, we shouldn't do anything

    var contexts = new(vector.Vector)

    switch val := valueInd.(type) {
    case *reflect.BoolValue:
        if !val.Get() {
            return
        } else {
            contexts.Push(context)
        }
    case *reflect.SliceValue:
        for i := 0; i < val.Len(); i++ {
            contexts.Push(val.Elem(i))
        }
    case *reflect.ArrayValue:
        for i := 0; i < val.Len(); i++ {
            contexts.Push(val.Elem(i))
        }
    default:
        contexts.Push(context)
    }

    //by default we execute the section
    for j := 0; j < contexts.Len(); j++ {
        ctx := contexts.At(j).(reflect.Value)
        for i := 0; i < section.elems.Len(); i++ {
            renderElement(section.elems.At(i), ctx, buf)
        }
    }
}

func renderElement(element interface{}, context reflect.Value, buf io.Writer) {

    switch elem := element.(type) {
    case *textElement:
        buf.Write(elem.text)
    case *varElement:
        val := lookup(context, elem.name)
        if val != nil {
            fmt.Fprint(buf, val.Interface())
        }
    case *sectionElement:
        renderSection(elem, context, buf)
    case *Template:
        elem.renderTemplate(context, buf)
    }
}

func (tmpl *Template) renderTemplate(context reflect.Value, buf io.Writer) {
    for i := 0; i < tmpl.elems.Len(); i++ {
        renderElement(tmpl.elems.At(i), context, buf)
    }
}

func (tmpl *Template) Render(context interface{}, buf io.Writer) {
    val := reflect.NewValue(context)
    tmpl.renderTemplate(val, buf)
}

func ParseString(data string) (*Template, os.Error) {
    cwd := os.Getenv("CWD")
    tmpl := Template{data, "{{", "}}", 0, 1, cwd, new(vector.Vector)}
    err := tmpl.parse()

    if err != nil {
        return nil, err
    }

    return &tmpl, err
}

func ParseFile(filename string) (*Template, os.Error) {
    data, err := ioutil.ReadFile(filename)

    if err != nil {
        return nil, err
    }

    dirname, _ := path.Split(filename)

    tmpl := Template{string(data), "{{", "}}", 0, 1, dirname, new(vector.Vector)}
    err = tmpl.parse()

    if err != nil {
        return nil, err
    }

    return &tmpl, nil
}

func Render(data string, context interface{}) (string, os.Error) {
    tmpl, err := ParseString(data)

    if err != nil {
        return "", err
    }

    var buf bytes.Buffer
    tmpl.Render(context, &buf)

    return buf.String(), nil
}

func RenderFile(filename string, context interface{}) (string, os.Error) {
    tmpl, err := ParseFile(filename)

    if err != nil {
        return "", err
    }

    var buf bytes.Buffer
    tmpl.Render(context, &buf)

    return buf.String(), nil
}
<|endoftext|>


<commit_before>package vspherejanitor

import (
	"context"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/honeycombio/libhoney-go"
	"github.com/pkg/errors"
	"github.com/rcrowley/go-metrics"
	"github.com/travis-ci/vsphere-janitor/log"
)

type Janitor struct {
	vmLister VMLister
	opts     *JanitorOpts

	zeroUptimeFirstSeenMutex sync.Mutex
	zeroUptimeFirstSeen      map[string]time.Time
}

func NewJanitor(vmLister VMLister, opts *JanitorOpts) *Janitor {
	if opts == nil {
		opts = &JanitorOpts{
			Cutoff:         2 * time.Hour,
			Concurrency:    1,
			RatePerSecond:  5,
			SkipNoBootTime: true,
		}
	}

	return &Janitor{
		vmLister:            vmLister,
		opts:                opts,
		zeroUptimeFirstSeen: make(map[string]time.Time),
	}
}

type JanitorOpts struct {
	Cutoff           time.Duration
	ZeroUptimeCutoff time.Duration
	SkipDestroy      bool
	Concurrency      int
	RatePerSecond    int
	SkipNoBootTime   bool
}

func (j *Janitor) Cleanup(ctx context.Context, path string, now time.Time) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	sem := make(chan struct{}, j.opts.Concurrency)
	wg := sync.WaitGroup{}
	throttle := time.NewTicker(time.Second / time.Duration(j.opts.RatePerSecond))
	defer throttle.Stop()

	vms, err := j.vmLister.ListVMs(ctx, path)
	if err != nil {
		return errors.Wrap(err, "couldn't list VMs")
	}

	for _, vm := range vms {
		<-throttle.C

		err := j.handleVM(ctx, vm, &wg, sem, now)
		if err != nil {
			log.WithContext(ctx).WithError(err).Error("error handling VM")
		}
	}

	j.cleanupFirstSeen(vms)

	wg.Wait()

	metrics.GetOrRegisterGauge("vsphere.janitor.cleanup.vms.total", metrics.DefaultRegistry).Update(int64(len(vms)))
	return nil
}

func (j *Janitor) cleanupFirstSeen(vms []VirtualMachine) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()

	vmExists := make(map[string]bool, len(vms))
	for _, vm := range vms {
		vmExists[vm.ID()] = true
	}

	for id := range j.zeroUptimeFirstSeen {
		if !vmExists[id] {
			delete(j.zeroUptimeFirstSeen, id)
		}
	}
}

func (j *Janitor) handleVM(ctx context.Context,
	vm VirtualMachine, wg *sync.WaitGroup, sem chan (struct{}), now time.Time) (err error) {
	logger := log.WithContext(ctx).WithField("vm", vm.Name())
	event := libhoney.NewEvent()
	event.AddField("meta.type", "cleanup")
	event.AddField("app.vm_id", vm.ID())
	event.AddField("app.vm_name", vm.Name())

	defer func() {
		panicErr := recover()
		if panicErr != nil {
			err = panicErr.(error)
		}
	}()

	uptimeSecs := int(vm.Uptime().Seconds())

	if uptimeSecs == 0 && vm.BootTime() == nil {
		if vm.ID() == "" {
			logger.Info("VM doesn't have ID yet, skipping")
			return nil
		}

		firstSeen, ok := j.getZeroUptimeFirstSeen(vm.ID())
		if !ok || now.Sub(firstSeen) < j.opts.ZeroUptimeCutoff {
			logger.Info("instance has 0 uptime, skipping for now")
			if !ok {
				j.setZeroUptimeFirstSeen(vm.ID(), now)
			}
			return nil
		}

		event.AddField("app.since_first_seen", time.Since(firstSeen))
		j.deleteZeroUptimeFirstSeen(vm.ID())
		logger.WithField("since_first_seen", time.Since(firstSeen)).Info("instance has had 0 uptime for more than timeout, destroying")
	} else {
		bootTime := vm.BootTime()

		if j.opts.SkipNoBootTime && bootTime == nil {
			logger.Info("instance has no boot time, skipping")
			return nil
		}

		if bootTime != nil {
			bootedAgo := now.UTC().Sub(*bootTime)
			event.AddField("app.since_boot", bootedAgo)
			logger.WithField("booted_ago", bootedAgo).Info("time since instance boot")
		}

		uptime := time.Duration(uptimeSecs) * time.Second
		event.AddField("app.uptime", uptime)
		if uptime < j.opts.Cutoff && vm.PoweredOn() {
			logger.WithField("uptime", uptime).WithField("powered_on", vm.PoweredOn()).Info("skipping instance")
			return nil
		}
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		err := j.powerOffAndDestroy(ctx, logger, sem, vm)
		if err != nil {
			event.AddField("app.err", err.Error())
			logger.WithError(err).Error("error powering off and destroying instance")
		}

		// only send events if we actually cleaned up the VM
		event.Send()
	}()
	return nil
}

func (j *Janitor) powerOffAndDestroy(ctx context.Context, logger logrus.FieldLogger, sem chan (struct{}), vm VirtualMachine) (err error) {
	sem <- struct{}{}
	defer func() {
		panicErr := recover()
		if panicErr != nil {
			err = panicErr.(error)
		}
		<-sem
	}()

	logger.WithField("uptime", vm.Uptime()).Info("handling poweroff and destroy of instance")

	if vm.PoweredOn() {
		logger.Info("powering off instance")

		err := vm.PowerOff(ctx)
		if err != nil {
			return errors.Wrap(err, "error powering off VM")
		}

		metrics.GetOrRegisterMeter("vsphere.janitor.cleanup.vms.poweroff", metrics.DefaultRegistry).Mark(1)
	}

	if j.opts.SkipDestroy {
		logger.Info("skipping destroy step")
		return nil
	}

	logger.Info("destroying instance")

	err = vm.Destroy(ctx)
	if err != nil {
		return errors.Wrap(err, "error destroying VM")
	}

	logger.Info("destroyed instance")
	metrics.GetOrRegisterMeter("vsphere.janitor.cleanup.vms.destroy", metrics.DefaultRegistry).Mark(1)

	return nil
}

func (j *Janitor) getZeroUptimeFirstSeen(id string) (time.Time, bool) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()
	firstSeen, ok := j.zeroUptimeFirstSeen[id]
	return firstSeen, ok
}

func (j *Janitor) setZeroUptimeFirstSeen(id string, now time.Time) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()
	j.zeroUptimeFirstSeen[id] = now
}

func (j *Janitor) deleteZeroUptimeFirstSeen(id string) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()
	delete(j.zeroUptimeFirstSeen, id)
}
<commit_msg>Add VM powered on Honeycomb field<commit_after>package vspherejanitor

import (
	"context"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/honeycombio/libhoney-go"
	"github.com/pkg/errors"
	"github.com/rcrowley/go-metrics"
	"github.com/travis-ci/vsphere-janitor/log"
)

type Janitor struct {
	vmLister VMLister
	opts     *JanitorOpts

	zeroUptimeFirstSeenMutex sync.Mutex
	zeroUptimeFirstSeen      map[string]time.Time
}

func NewJanitor(vmLister VMLister, opts *JanitorOpts) *Janitor {
	if opts == nil {
		opts = &JanitorOpts{
			Cutoff:         2 * time.Hour,
			Concurrency:    1,
			RatePerSecond:  5,
			SkipNoBootTime: true,
		}
	}

	return &Janitor{
		vmLister:            vmLister,
		opts:                opts,
		zeroUptimeFirstSeen: make(map[string]time.Time),
	}
}

type JanitorOpts struct {
	Cutoff           time.Duration
	ZeroUptimeCutoff time.Duration
	SkipDestroy      bool
	Concurrency      int
	RatePerSecond    int
	SkipNoBootTime   bool
}

func (j *Janitor) Cleanup(ctx context.Context, path string, now time.Time) error {
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	sem := make(chan struct{}, j.opts.Concurrency)
	wg := sync.WaitGroup{}
	throttle := time.NewTicker(time.Second / time.Duration(j.opts.RatePerSecond))
	defer throttle.Stop()

	vms, err := j.vmLister.ListVMs(ctx, path)
	if err != nil {
		return errors.Wrap(err, "couldn't list VMs")
	}

	for _, vm := range vms {
		<-throttle.C

		err := j.handleVM(ctx, vm, &wg, sem, now)
		if err != nil {
			log.WithContext(ctx).WithError(err).Error("error handling VM")
		}
	}

	j.cleanupFirstSeen(vms)

	wg.Wait()

	metrics.GetOrRegisterGauge("vsphere.janitor.cleanup.vms.total", metrics.DefaultRegistry).Update(int64(len(vms)))
	return nil
}

func (j *Janitor) cleanupFirstSeen(vms []VirtualMachine) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()

	vmExists := make(map[string]bool, len(vms))
	for _, vm := range vms {
		vmExists[vm.ID()] = true
	}

	for id := range j.zeroUptimeFirstSeen {
		if !vmExists[id] {
			delete(j.zeroUptimeFirstSeen, id)
		}
	}
}

func (j *Janitor) handleVM(ctx context.Context,
	vm VirtualMachine, wg *sync.WaitGroup, sem chan (struct{}), now time.Time) (err error) {
	logger := log.WithContext(ctx).WithField("vm", vm.Name())
	event := libhoney.NewEvent()
	event.AddField("meta.type", "cleanup")
	event.AddField("app.vm_id", vm.ID())
	event.AddField("app.vm_name", vm.Name())
	event.AddField("app.powered_on", vm.PoweredOn())

	defer func() {
		panicErr := recover()
		if panicErr != nil {
			err = panicErr.(error)
		}
	}()

	uptimeSecs := int(vm.Uptime().Seconds())

	if uptimeSecs == 0 && vm.BootTime() == nil {
		if vm.ID() == "" {
			logger.Info("VM doesn't have ID yet, skipping")
			return nil
		}

		firstSeen, ok := j.getZeroUptimeFirstSeen(vm.ID())
		if !ok || now.Sub(firstSeen) < j.opts.ZeroUptimeCutoff {
			logger.Info("instance has 0 uptime, skipping for now")
			if !ok {
				j.setZeroUptimeFirstSeen(vm.ID(), now)
			}
			return nil
		}

		event.AddField("app.since_first_seen", time.Since(firstSeen))
		j.deleteZeroUptimeFirstSeen(vm.ID())
		logger.WithField("since_first_seen", time.Since(firstSeen)).Info("instance has had 0 uptime for more than timeout, destroying")
	} else {
		bootTime := vm.BootTime()

		if j.opts.SkipNoBootTime && bootTime == nil {
			logger.Info("instance has no boot time, skipping")
			return nil
		}

		if bootTime != nil {
			bootedAgo := now.UTC().Sub(*bootTime)
			event.AddField("app.since_boot", bootedAgo/time.Second)
			logger.WithField("booted_ago", bootedAgo).Info("time since instance boot")
		}

		uptime := time.Duration(uptimeSecs) * time.Second
		event.AddField("app.uptime", uptimeSecs)
		if uptime < j.opts.Cutoff && vm.PoweredOn() {
			logger.WithField("uptime", uptime).WithField("powered_on", vm.PoweredOn()).Info("skipping instance")
			return nil
		}
	}

	wg.Add(1)
	go func() {
		defer wg.Done()
		err := j.powerOffAndDestroy(ctx, logger, sem, vm)
		if err != nil {
			event.AddField("app.err", err.Error())
			logger.WithError(err).Error("error powering off and destroying instance")
		}

		// only send events if we actually cleaned up the VM
		event.Send()
	}()
	return nil
}

func (j *Janitor) powerOffAndDestroy(ctx context.Context, logger logrus.FieldLogger, sem chan (struct{}), vm VirtualMachine) (err error) {
	sem <- struct{}{}
	defer func() {
		panicErr := recover()
		if panicErr != nil {
			err = panicErr.(error)
		}
		<-sem
	}()

	logger.WithField("uptime", vm.Uptime()).Info("handling poweroff and destroy of instance")

	if vm.PoweredOn() {
		logger.Info("powering off instance")

		err := vm.PowerOff(ctx)
		if err != nil {
			return errors.Wrap(err, "error powering off VM")
		}

		metrics.GetOrRegisterMeter("vsphere.janitor.cleanup.vms.poweroff", metrics.DefaultRegistry).Mark(1)
	}

	if j.opts.SkipDestroy {
		logger.Info("skipping destroy step")
		return nil
	}

	logger.Info("destroying instance")

	err = vm.Destroy(ctx)
	if err != nil {
		return errors.Wrap(err, "error destroying VM")
	}

	logger.Info("destroyed instance")
	metrics.GetOrRegisterMeter("vsphere.janitor.cleanup.vms.destroy", metrics.DefaultRegistry).Mark(1)

	return nil
}

func (j *Janitor) getZeroUptimeFirstSeen(id string) (time.Time, bool) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()
	firstSeen, ok := j.zeroUptimeFirstSeen[id]
	return firstSeen, ok
}

func (j *Janitor) setZeroUptimeFirstSeen(id string, now time.Time) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()
	j.zeroUptimeFirstSeen[id] = now
}

func (j *Janitor) deleteZeroUptimeFirstSeen(id string) {
	j.zeroUptimeFirstSeenMutex.Lock()
	defer j.zeroUptimeFirstSeenMutex.Unlock()
	delete(j.zeroUptimeFirstSeen, id)
}
<|endoftext|>


<commit_before><commit_msg>Fix invalid string value with EOS in HPACK 5.2.3<commit_after><|endoftext|>


<commit_before>package main

func main() {}

type T struct {
	I int64   `json:"i,omitempty"`
	F float64 `json:"f,omitempty"`
	B bool    `json:"b,omitempy"`
	S string  `json:"s,omitempty"`
}
<commit_msg>rename One<commit_after>package main

func main() {}

type One struct {
	I int64   `json:"i,omitempty"`
	F float64 `json:"f,omitempty"`
	B bool    `json:"b,omitempy"`
	S string  `json:"s,omitempty"`
}
<|endoftext|>


<commit_before>package strategy

import (
	"sync"

	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

	bstore "github.com/jbenet/go-ipfs/blocks/blockstore"
	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
	wl "github.com/jbenet/go-ipfs/exchange/bitswap/wantlist"
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

var log = u.Logger("strategy")

// LedgerMap lists Ledgers by their Partner key.
type ledgerMap map[u.Key]*ledger

type Envelope struct {
	Peer    peer.Peer
	Message bsmsg.BitSwapMessage
}

type LedgerManager struct {
	lock      sync.RWMutex
	ledgerMap ledgerMap
	bs        bstore.Blockstore
	// FIXME tasklist isn't threadsafe nor is it protected by a mutex. consider
	// a way to avoid sharing the tasklist between the worker and the receiver
	tasklist   *taskList
	outbox     chan Envelope
	workSignal chan struct{}
}

func NewLedgerManager(ctx context.Context, bs bstore.Blockstore) *LedgerManager {
	lm := &LedgerManager{
		ledgerMap:  make(ledgerMap),
		bs:         bs,
		tasklist:   newTaskList(),
		outbox:     make(chan Envelope, 4), // TODO extract constant
		workSignal: make(chan struct{}),
	}
	go lm.taskWorker(ctx)
	return lm
}

func (lm *LedgerManager) taskWorker(ctx context.Context) {
	for {
		nextTask := lm.tasklist.Pop()
		if nextTask == nil {
			// No tasks in the list?
			// Wait until there are!
			select {
			case <-ctx.Done():
				return
			case <-lm.workSignal:
			}
			continue
		}
		block, err := lm.bs.Get(nextTask.Key)
		if err != nil {
			continue // TODO maybe return an error
		}
		// construct message here so we can make decisions about any additional
		// information we may want to include at this time.
		m := bsmsg.New()
		m.AddBlock(block)
		// TODO: maybe add keys from our wantlist?
		select {
		case <-ctx.Done():
			return
		case lm.outbox <- Envelope{Peer: nextTask.Target, Message: m}:
		}
	}
}

func (lm *LedgerManager) Outbox() <-chan Envelope {
	return lm.outbox
}

// Returns a slice of Peers with whom the local node has active sessions
func (lm *LedgerManager) Peers() []peer.Peer {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	response := make([]peer.Peer, 0)
	for _, ledger := range lm.ledgerMap {
		response = append(response, ledger.Partner)
	}
	return response
}

// BlockIsWantedByPeer returns true if peer wants the block given by this
// key
func (lm *LedgerManager) BlockIsWantedByPeer(k u.Key, p peer.Peer) bool {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	ledger := lm.findOrCreate(p)
	return ledger.WantListContains(k)
}

// MessageReceived performs book-keeping. Returns error if passed invalid
// arguments.
func (lm *LedgerManager) MessageReceived(p peer.Peer, m bsmsg.BitSwapMessage) error {
	lm.lock.Lock()
	defer lm.lock.Unlock()

	l := lm.findOrCreate(p)
	if m.Full() {
		l.wantList = wl.New()
	}
	for _, e := range m.Wantlist() {
		if e.Cancel {
			l.CancelWant(e.Key)
			lm.tasklist.Cancel(e.Key, p)
		} else {
			l.Wants(e.Key, e.Priority)
			lm.tasklist.Push(e.Key, e.Priority, p)

			// Signal task generation to restart (if stopped!)
			select {
			case lm.workSignal <- struct{}{}:
			default:
			}
		}
	}

	for _, block := range m.Blocks() {
		// FIXME extract blocks.NumBytes(block) or block.NumBytes() method
		l.ReceivedBytes(len(block.Data))
		for _, l := range lm.ledgerMap {
			if l.WantListContains(block.Key()) {
				lm.tasklist.Push(block.Key(), 1, l.Partner)

				// Signal task generation to restart (if stopped!)
				select {
				case lm.workSignal <- struct{}{}:
				default:
				}

			}
		}
	}
	return nil
}

// TODO add contents of m.WantList() to my local wantlist? NB: could introduce
// race conditions where I send a message, but MessageSent gets handled after
// MessageReceived. The information in the local wantlist could become
// inconsistent. Would need to ensure that Sends and acknowledgement of the
// send happen atomically

func (lm *LedgerManager) MessageSent(p peer.Peer, m bsmsg.BitSwapMessage) error {
	lm.lock.Lock()
	defer lm.lock.Unlock()

	l := lm.findOrCreate(p)
	for _, block := range m.Blocks() {
		l.SentBytes(len(block.Data))
		l.wantList.Remove(block.Key())
		lm.tasklist.Cancel(block.Key(), p)
	}

	return nil
}

func (lm *LedgerManager) NumBytesSentTo(p peer.Peer) uint64 {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	return lm.findOrCreate(p).Accounting.BytesSent
}

func (lm *LedgerManager) NumBytesReceivedFrom(p peer.Peer) uint64 {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	return lm.findOrCreate(p).Accounting.BytesRecv
}

// ledger lazily instantiates a ledger
func (lm *LedgerManager) findOrCreate(p peer.Peer) *ledger {
	l, ok := lm.ledgerMap[p.Key()]
	if !ok {
		l = newLedger(p)
		lm.ledgerMap[p.Key()] = l
	}
	return l
}
<commit_msg>perf: avoid lots of communication by signaling once at end of method<commit_after>package strategy

import (
	"sync"

	"github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"

	bstore "github.com/jbenet/go-ipfs/blocks/blockstore"
	bsmsg "github.com/jbenet/go-ipfs/exchange/bitswap/message"
	wl "github.com/jbenet/go-ipfs/exchange/bitswap/wantlist"
	peer "github.com/jbenet/go-ipfs/peer"
	u "github.com/jbenet/go-ipfs/util"
)

var log = u.Logger("strategy")

// LedgerMap lists Ledgers by their Partner key.
type ledgerMap map[u.Key]*ledger

type Envelope struct {
	Peer    peer.Peer
	Message bsmsg.BitSwapMessage
}

type LedgerManager struct {
	lock      sync.RWMutex
	ledgerMap ledgerMap
	bs        bstore.Blockstore
	// FIXME tasklist isn't threadsafe nor is it protected by a mutex. consider
	// a way to avoid sharing the tasklist between the worker and the receiver
	tasklist   *taskList
	outbox     chan Envelope
	workSignal chan struct{}
}

func NewLedgerManager(ctx context.Context, bs bstore.Blockstore) *LedgerManager {
	lm := &LedgerManager{
		ledgerMap:  make(ledgerMap),
		bs:         bs,
		tasklist:   newTaskList(),
		outbox:     make(chan Envelope, 4), // TODO extract constant
		workSignal: make(chan struct{}),
	}
	go lm.taskWorker(ctx)
	return lm
}

func (lm *LedgerManager) taskWorker(ctx context.Context) {
	for {
		nextTask := lm.tasklist.Pop()
		if nextTask == nil {
			// No tasks in the list?
			// Wait until there are!
			select {
			case <-ctx.Done():
				return
			case <-lm.workSignal:
			}
			continue
		}
		block, err := lm.bs.Get(nextTask.Key)
		if err != nil {
			continue // TODO maybe return an error
		}
		// construct message here so we can make decisions about any additional
		// information we may want to include at this time.
		m := bsmsg.New()
		m.AddBlock(block)
		// TODO: maybe add keys from our wantlist?
		select {
		case <-ctx.Done():
			return
		case lm.outbox <- Envelope{Peer: nextTask.Target, Message: m}:
		}
	}
}

func (lm *LedgerManager) Outbox() <-chan Envelope {
	return lm.outbox
}

// Returns a slice of Peers with whom the local node has active sessions
func (lm *LedgerManager) Peers() []peer.Peer {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	response := make([]peer.Peer, 0)
	for _, ledger := range lm.ledgerMap {
		response = append(response, ledger.Partner)
	}
	return response
}

// BlockIsWantedByPeer returns true if peer wants the block given by this
// key
func (lm *LedgerManager) BlockIsWantedByPeer(k u.Key, p peer.Peer) bool {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	ledger := lm.findOrCreate(p)
	return ledger.WantListContains(k)
}

// MessageReceived performs book-keeping. Returns error if passed invalid
// arguments.
func (lm *LedgerManager) MessageReceived(p peer.Peer, m bsmsg.BitSwapMessage) error {
	newWorkExists := false
	defer func() {
		if newWorkExists {
			// Signal task generation to restart (if stopped!)
			select {
			case lm.workSignal <- struct{}{}:
			default:
			}
		}
	}()
	lm.lock.Lock()
	defer lm.lock.Unlock()

	l := lm.findOrCreate(p)
	if m.Full() {
		l.wantList = wl.New()
	}
	for _, e := range m.Wantlist() {
		if e.Cancel {
			l.CancelWant(e.Key)
			lm.tasklist.Cancel(e.Key, p)
		} else {
			l.Wants(e.Key, e.Priority)
			newWorkExists = true
			lm.tasklist.Push(e.Key, e.Priority, p)
		}
	}

	for _, block := range m.Blocks() {
		// FIXME extract blocks.NumBytes(block) or block.NumBytes() method
		l.ReceivedBytes(len(block.Data))
		for _, l := range lm.ledgerMap {
			if l.WantListContains(block.Key()) {
				newWorkExists = true
				lm.tasklist.Push(block.Key(), 1, l.Partner)
			}
		}
	}
	return nil
}

// TODO add contents of m.WantList() to my local wantlist? NB: could introduce
// race conditions where I send a message, but MessageSent gets handled after
// MessageReceived. The information in the local wantlist could become
// inconsistent. Would need to ensure that Sends and acknowledgement of the
// send happen atomically

func (lm *LedgerManager) MessageSent(p peer.Peer, m bsmsg.BitSwapMessage) error {
	lm.lock.Lock()
	defer lm.lock.Unlock()

	l := lm.findOrCreate(p)
	for _, block := range m.Blocks() {
		l.SentBytes(len(block.Data))
		l.wantList.Remove(block.Key())
		lm.tasklist.Cancel(block.Key(), p)
	}

	return nil
}

func (lm *LedgerManager) NumBytesSentTo(p peer.Peer) uint64 {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	return lm.findOrCreate(p).Accounting.BytesSent
}

func (lm *LedgerManager) NumBytesReceivedFrom(p peer.Peer) uint64 {
	lm.lock.RLock()
	defer lm.lock.RUnlock()

	return lm.findOrCreate(p).Accounting.BytesRecv
}

// ledger lazily instantiates a ledger
func (lm *LedgerManager) findOrCreate(p peer.Peer) *ledger {
	l, ok := lm.ledgerMap[p.Key()]
	if !ok {
		l = newLedger(p)
		lm.ledgerMap[p.Key()] = l
	}
	return l
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"testing"
	"time"
)

const (
	LED      = 5  // Pin 29
	RELAY    = 13 // Pin 33
	CAP4700  = 19 // Pin 35: Also PCM capable
	CAP10000 = 6  // Pin 31
	SWITCH   = 26 // Pin 37
)

func GpioStr(g PiPin) string {
	switch g.Pin() {
	case LED:
		return "LED"
	case RELAY:
		return "RELAY"
	case CAP4700:
		return "CAP4700"
	case CAP10000:
		return "CAP10000"
	case SWITCH:
		return "SWITCH"
	default:
		return "UNKNOWN"
	}
	return ""
}

var Led PiPin        // Setup: GPIO -> <1k Resistor -> LED -> GND
var TestRelay *Relay // Setup GPIO -> 4.7k Resistor -> Relay Board
var Cap4700 PiPin    // Setup: +3.3v -> 4.7k Resistor -> GPIO -> 10uF capacitor -> GND
var Cap10000 PiPin   // Setup: +3.3v -> 10k Resistor -> GPIO -> 10uF capacitor -> GND
var Switch PiPin     // Setup: GPIO -> Button Switch -> GND

func ExpectedState(t *testing.T, gpio PiPin, exp GpioState) {
	if val := gpio.Read(); val != exp {
		t.Errorf("%s: Expected %s but found %s", GpioStr(gpio), exp, val)
	}
}

func TestInitilization(t *testing.T) {
	err := GpioInit()
	t.Run("Init Host", func(t *testing.T) {
		if err != nil {
			t.Errorf("Problem initializing gpio: %s", err.Error())
		}
	})

	// Initialized GPIOs
	Led = NewGpio(LED)
	ExpectedState(t, Led, Low)
}

func TestBlinkLed(t *testing.T) {
	Info("Running %s", t.Name())
	for i := 0; i < 6; i++ {
		time.Sleep(time.Second / 5)
		Led.Output(High)
		ExpectedState(t, Led, High)
		time.Sleep(time.Second / 5)
		Led.Output(Low)
		ExpectedState(t, Led, Low)
	}
}

func doStop(button *Button, b *bool, t time.Time) {
	*b = false
	button.Stop()
	*b = true
	Info("doStop - Stopped after %d ms", time.Now().Sub(t)/time.Millisecond)
}

func runRelayTestOn(t *testing.T, relay *Relay) {
	relay.TurnOn()
	Info("Testing Relay On: %s is %s", relay.Name(), relay.Status())
	if !relay.isOn() {
		t.Errorf("Relay(%s) is %s", relay.Name(), relay.Status())
	}
}

func runRelayTestOff(t *testing.T, relay *Relay) {
	relay.TurnOff()
	Info("Testing Relay Off: %s is %s", relay.Name(), relay.Status())
	if relay.isOn() {
		t.Errorf("Relay(%s) is %s", relay.Name(), relay.Status())
	}
}

func runRelayTest(t *testing.T, r *Relay, sleep time.Duration) {
	t.Run(fmt.Sprintf("%s.Test", r.Name()), func(t *testing.T) {
		Info("Running %s", t.Name())
		runRelayTestOn(t, r)
		time.Sleep(sleep)
		runRelayTestOff(t, r)
	})
}

func TestRelays(t *testing.T) {
	Info("Running %s", t.Name())
	TestRelay = NewRelay(RELAY, "Relay", "Testing")
	runRelayTest(t, TestRelay, time.Second)
}

func discharge_us(t *GpioThermometer, e Edge, p Pull) time.Duration {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	//Discharge the capacitor (low temps could make this really long)
	t.pin.Output(Low)
	time.Sleep(300 * time.Millisecond)

	// Start polling
	start := time.Now()
	t.pin.InputEdge(p, e)
	if !t.pin.WaitForEdge(time.Second / 2) {
		Trace("Thermometer %s, Rising read timed out", t.Name())
		return 0.0
	}
	stop := time.Now()
	t.pin.Output(Low)
	return stop.Sub(start)
}

func TestDischargeStrategies(t *testing.T) {
	Info("Running %s", t.Name())
	therm := NewGpioThermometer("Fixed 4.7kOhm ResistorTest", "TestManufacturer", CAP4700)
	pulls := []Pull{PullDown, PullUp, Float}
	edges := []Edge{RisingEdge, FallingEdge, BothEdges}
	expected := 4700 * therm.microfarads
	for _, p := range pulls {
		for _, e := range edges {
			h := NewHistory(10)
			for i := 0; i < 10; i++ {
				dt := discharge_us(therm, e, p)
				//Info("DischargeTime %f us,  %f k-ohms", us(dt), therm.getOhms(dt))
				h.Push(us(dt))
			}
			Info("Strategy(%s, %s): Expected %0.3fus %0.3fus stddev=%0.4f pct=%0.2f",
				p, e, expected, h.Average(), h.Stddev(), 100.0*h.Stddev()/h.Average())
		}
	}
}

func TestThermometer(t *testing.T) {
	t.Skip("Skipping TestThermometer until it is fixed")
	Info("Running %s", t.Name())
	therm := NewGpioThermometer("Fixed 4.7kOhm ResistorTest", "TestManufacturer", CAP4700)

	t.Run("Calibrate Cap4700", func(t *testing.T) {
		Info("Running %s", t.Name())
		c, err := therm.Calibrate(4700)
		if err != nil {
			t.Errorf("Failure to Calibrate successfully: %s", err.Error())
		}
		Debug("Setting calibration for %0.3f", c)
		therm.SetAdjustment(c)
	})
	t.Run("Temperature Cap4700", func(t *testing.T) {
		Info("Running %s", t.Name())
		err := therm.Update()
		if err != nil {
			t.Errorf("Thermometer update failed: %s", err.Error())
		}
		if therm.Temperature() > 44.1 || therm.Temperature() < 43.1 {
			t.Errorf("Thermometer value off: %0.1f, expected 43.6",
				therm.Temperature())
		}
	})

	therm = NewGpioThermometer("Fixed 10kOhm ResistorTest", "TestManufacturer", CAP10000)
	t.Run("Calibrate Cap10000", func(t *testing.T) {
		Info("Running %s", t.Name())
		c, err := therm.Calibrate(10000)
		if err != nil {
			t.Errorf("Failure to Calibrate successfully: %s", err.Error())
		}
		Debug("Setting calibration for %0.3f", c)
		therm.SetAdjustment(c)
	})
	t.Run("Temperature Cap4700", func(t *testing.T) {
		Info("Running %s", t.Name())
		err := therm.Update()
		if err != nil {
			t.Errorf("Thermometer update failed: %s", err.Error())
		}
		if therm.Temperature() > 25.4 || therm.Temperature() < 24.4 {
			t.Errorf("Thermometer value off: %0.1f, expected 24.9",
				therm.Temperature())
		}
	})
}

func TestPushButton(t *testing.T) {
	Info("Running %s", t.Name())
	wasRun := 0
	button := NewGpioButton(SWITCH, func() {
		wasRun++
		Info("Button Pushed %d!!!", wasRun)
	})

	Info("Starting button test, push it 3 times!")
	button.Start()
	for i := 0; i < 3; i++ {
		TestRelay.TurnOn()
		time.Sleep(time.Second / 3)
		TestRelay.TurnOff()
		time.Sleep(2 * time.Second)
	}
	if wasRun < 3 {
		t.Errorf("Expected 3 button pushes")
	}
	Info("Stopping button job")
	exited := false
	go doStop(button, &exited, time.Now())
	time.Sleep(time.Second)
	if !exited {
		t.Errorf("Button loop should have stopped within time allotted")
	}
	Info("Button job stopped")
}
<commit_msg>test sync<commit_after>package main

import (
	"fmt"
	"testing"
	"time"
)

const (
	LED      = 5  // Pin 29
	RELAY    = 13 // Pin 33
	CAP4700  = 19 // Pin 35: Also PCM capable
	CAP10000 = 6  // Pin 31
	SWITCH   = 26 // Pin 37
)

func GpioStr(g PiPin) string {
	switch g.Pin() {
	case LED:
		return "LED"
	case RELAY:
		return "RELAY"
	case CAP4700:
		return "CAP4700"
	case CAP10000:
		return "CAP10000"
	case SWITCH:
		return "SWITCH"
	default:
		return "UNKNOWN"
	}
	return ""
}

var Led PiPin        // Setup: GPIO -> <1k Resistor -> LED -> GND
var TestRelay *Relay // Setup GPIO -> 4.7k Resistor -> Relay Board
var Cap4700 PiPin    // Setup: +3.3v -> 4.7k Resistor -> GPIO -> 10uF capacitor -> GND
var Cap10000 PiPin   // Setup: +3.3v -> 10k Resistor -> GPIO -> 10uF capacitor -> GND
var Switch PiPin     // Setup: GPIO -> Button Switch -> GND

func ExpectedState(t *testing.T, gpio PiPin, exp GpioState) {
	if val := gpio.Read(); val != exp {
		t.Errorf("%s: Expected %s but found %s", GpioStr(gpio), exp, val)
	}
}

func TestInitilization(t *testing.T) {
	err := GpioInit()
	t.Run("Init Host", func(t *testing.T) {
		if err != nil {
			t.Errorf("Problem initializing gpio: %s", err.Error())
		}
	})

	// Initialized GPIOs
	Led = NewGpio(LED)
	ExpectedState(t, Led, Low)
}

func TestBlinkLed(t *testing.T) {
	Info("Running %s", t.Name())
	for i := 0; i < 6; i++ {
		time.Sleep(time.Second / 5)
		Led.Output(High)
		ExpectedState(t, Led, High)
		time.Sleep(time.Second / 5)
		Led.Output(Low)
		ExpectedState(t, Led, Low)
	}
}

func doStop(button *Button, b *bool, t time.Time) {
	*b = false
	button.Stop()
	*b = true
	Info("doStop - Stopped after %d ms", time.Now().Sub(t)/time.Millisecond)
}

func runRelayTestOn(t *testing.T, relay *Relay) {
	relay.TurnOn()
	Info("Testing Relay On: %s is %s", relay.Name(), relay.Status())
	if !relay.isOn() {
		t.Errorf("Relay(%s) is %s", relay.Name(), relay.Status())
	}
}

func runRelayTestOff(t *testing.T, relay *Relay) {
	relay.TurnOff()
	Info("Testing Relay Off: %s is %s", relay.Name(), relay.Status())
	if relay.isOn() {
		t.Errorf("Relay(%s) is %s", relay.Name(), relay.Status())
	}
}

func runRelayTest(t *testing.T, r *Relay, sleep time.Duration) {
	t.Run(fmt.Sprintf("%s.Test", r.Name()), func(t *testing.T) {
		Info("Running %s", t.Name())
		runRelayTestOn(t, r)
		time.Sleep(sleep)
		runRelayTestOff(t, r)
	})
}

func TestRelays(t *testing.T) {
	Info("Running %s", t.Name())
	TestRelay = NewRelay(RELAY, "Relay", "Testing")
	runRelayTest(t, TestRelay, time.Second)
}

func discharge_us(t *GpioThermometer, e Edge, p Pull) time.Duration {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	//Discharge the capacitor (low temps could make this really long)
	t.pin.Output(Low)
	time.Sleep(300 * time.Millisecond)

	// Start polling
	start := time.Now()
	t.pin.InputEdge(p, e)
	if !t.pin.WaitForEdge(time.Second / 2) {
		Trace("Thermometer %s, Rising read timed out", t.Name())
		return 0.0
	}
	stop := time.Now()
	t.pin.Output(Low)
	return stop.Sub(start)
}

func TestDischargeStrategies(t *testing.T) {
	Info("Running %s", t.Name())
	therm := NewGpioThermometer("Fixed 4.7kOhm ResistorTest", "TestManufacturer", CAP4700)
	pulls := []Pull{PullDown, PullUp, Float}
	edges := []Edge{RisingEdge, FallingEdge, BothEdges}
	expected := 4700 * therm.microfarads
	for _, p := range pulls {
		for _, e := range edges {
			h := NewHistory(10)
			for i := 0; i < 10; i++ {
				dt := discharge_us(therm, e, p)
				//Info("DischargeTime %f us,  %f k-ohms", us(dt), therm.getOhms(dt))
				h.Push(us(dt))
			}
			Info("Strategy(%s, %s): Expected %0.3fus %0.3fus stddev=%0.4f pct=%0.2f",
				p, e, expected, h.Average(), h.Stddev(), 100.0*h.Stddev()/h.Average())
		}
	}
}

func TestThermometer(t *testing.T) {
	t.Skip("Skipping TestThermometer until it is fixed")
	Info("Running %s", t.Name())
	therm := NewGpioThermometer("Fixed 4.7kOhm ResistorTest", "TestManufacturer", CAP4700)

	t.Run("Calibrate Cap4700", func(t *testing.T) {
		Info("Running %s", t.Name())
		c, err := therm.Calibrate(4700)
		if err != nil {
			t.Errorf("Failure to Calibrate successfully: %s", err.Error())
		}
		Debug("Setting calibration for %0.3f", c)
		therm.SetAdjustment(c)
	})
	t.Run("Temperature Cap4700", func(t *testing.T) {
		Info("Running %s", t.Name())
		err := therm.Update()
		if err != nil {
			t.Errorf("Thermometer update failed: %s", err.Error())
		}
		if therm.Temperature() > 44.1 || therm.Temperature() < 43.1 {
			t.Errorf("Thermometer value off: %0.1f, expected 43.6",
				therm.Temperature())
		}
	})

	therm = NewGpioThermometer("Fixed 10kOhm ResistorTest", "TestManufacturer", CAP10000)
	t.Run("Calibrate Cap10000", func(t *testing.T) {
		Info("Running %s", t.Name())
		c, err := therm.Calibrate(10000)
		if err != nil {
			t.Errorf("Failure to Calibrate successfully: %s", err.Error())
		}
		Debug("Setting calibration for %0.3f", c)
		therm.SetAdjustment(c)
	})
	t.Run("Temperature Cap4700", func(t *testing.T) {
		Info("Running %s", t.Name())
		err := therm.Update()
		if err != nil {
			t.Errorf("Thermometer update failed: %s", err.Error())
		}
		if therm.Temperature() > 25.4 || therm.Temperature() < 24.4 {
			t.Errorf("Thermometer value off: %0.1f, expected 24.9",
				therm.Temperature())
		}
	})
}

func TestPushButton(t *testing.T) {
	Info("Running %s", t.Name())
	wasRun := 0
	button := NewGpioButton(SWITCH, func() {
		wasRun++
		Led.Output(High)
		Info("Button Pushed %d!!!", wasRun)
	})

	Info("Starting button test, push it 3 times!")
	button.Start()
	for i := 0; i < 3; i++ {
		TestRelay.TurnOn()
		time.Sleep(time.Second / 3)
		TestRelay.TurnOff()
		Led.Output(Low)
		time.Sleep(2 * time.Second)
	}
	if wasRun < 3 {
		t.Errorf("Expected 3 button pushes")
	}
	Info("Stopping button job")
	exited := false
	go doStop(button, &exited, time.Now())
	time.Sleep(time.Second)
	if !exited {
		t.Errorf("Button loop should have stopped within time allotted")
	}
	Info("Button job stopped")
}
<|endoftext|>


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

package client_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver/client"
	"launchpad.net/juju-core/testing"
	jc "launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/utils/exec"
	"launchpad.net/juju-core/utils/ssh"
)

type runSuite struct {
	baseSuite
}

var _ = gc.Suite(&runSuite{})

func (s *runSuite) addMachine(c *gc.C) *state.Machine {
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	return machine
}

func (s *runSuite) addMachineWithAddress(c *gc.C, address string) *state.Machine {
	machine := s.addMachine(c)
	machine.SetAddresses([]instance.Address{instance.NewAddress(address)})
	return machine
}

func (s *runSuite) TestRemoteParamsForMachinePopulates(c *gc.C) {
	machine := s.addMachine(c)
	result := client.RemoteParamsForMachine(machine, "command", time.Minute)
	c.Assert(result.Command, gc.Equals, "command")
	c.Assert(result.Timeout, gc.Equals, time.Minute)
	c.Assert(result.MachineId, gc.Equals, machine.Id())
	// Now an empty host isn't particularly useful, but the machine doesn't
	// have an address to use.
	c.Assert(machine.Addresses(), gc.HasLen, 0)
	c.Assert(result.Host, gc.Equals, "")
}

func (s *runSuite) TestRemoteParamsForMachinePopulatesWithAddress(c *gc.C) {
	machine := s.addMachineWithAddress(c, "10.3.2.1")

	result := client.RemoteParamsForMachine(machine, "command", time.Minute)
	c.Assert(result.Command, gc.Equals, "command")
	c.Assert(result.Timeout, gc.Equals, time.Minute)
	c.Assert(result.MachineId, gc.Equals, machine.Id())
	c.Assert(result.Host, gc.Equals, "ubuntu@10.3.2.1")
}

func (s *runSuite) addUnit(c *gc.C, service *state.Service) *state.Unit {
	unit, err := service.AddUnit()
	c.Assert(err, gc.IsNil)
	err = unit.AssignToNewMachine()
	c.Assert(err, gc.IsNil)
	mId, err := unit.AssignedMachineId()
	c.Assert(err, gc.IsNil)
	machine, err := s.State.Machine(mId)
	c.Assert(err, gc.IsNil)
	machine.SetAddresses([]instance.Address{instance.NewAddress("10.3.2.1")})
	return unit
}

func (s *runSuite) TestGetAllUnitNames(c *gc.C) {
	charm := s.AddTestingCharm(c, "dummy")
	magic, err := s.State.AddService("magic", "user-admin", charm)
	s.addUnit(c, magic)
	s.addUnit(c, magic)

	notAssigned, err := s.State.AddService("not-assigned", "user-admin", charm)
	c.Assert(err, gc.IsNil)
	_, err = notAssigned.AddUnit()
	c.Assert(err, gc.IsNil)

	_, err = s.State.AddService("no-units", "user-admin", charm)
	c.Assert(err, gc.IsNil)

	for i, test := range []struct {
		message  string
		expected []string
		units    []string
		services []string
		error    string
	}{{
		message: "no units, expected nil slice",
	}, {
		message: "asking for a unit that isn't there",
		units:   []string{"foo/0"},
		error:   `unit "foo/0" not found`,
	}, {
		message:  "asking for a service that isn't there",
		services: []string{"foo"},
		error:    `service "foo" not found`,
	}, {
		message:  "service with no units is not really an error",
		services: []string{"no-units"},
	}, {
		message:  "A service with units not assigned is an error",
		services: []string{"not-assigned"},
		error:    `unit "not-assigned/0" is not assigned to a machine`,
	}, {
		message:  "A service with units",
		services: []string{"magic"},
		expected: []string{"magic/0", "magic/1"},
	}, {
		message:  "Asking for just a unit",
		units:    []string{"magic/0"},
		expected: []string{"magic/0"},
	}, {
		message:  "Asking for a unit, and the service",
		services: []string{"magic"},
		units:    []string{"magic/0"},
		expected: []string{"magic/0", "magic/1"},
	}} {
		c.Logf("%v: %s", i, test.message)
		result, err := client.GetAllUnitNames(s.State, test.units, test.services)
		if test.error == "" {
			c.Check(err, gc.IsNil)
			var units []string
			for _, unit := range result {
				units = append(units, unit.Name())
			}
			c.Check(units, jc.SameContents, test.expected)
		} else {
			c.Check(err, gc.ErrorMatches, test.error)
		}
	}
}

func (s *runSuite) mockSSH(c *gc.C, cmd string) {
	testbin := c.MkDir()
	fakessh := filepath.Join(testbin, "ssh")
	newPath := testbin + ":" + os.Getenv("PATH")
	s.PatchEnvironment("PATH", newPath)
	err := ioutil.WriteFile(fakessh, []byte(cmd), 0755)
	c.Assert(err, gc.IsNil)
}

func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) {
	s.mockSSH(c, echoInputShowArgs)

	params := []*client.RemoteExec{
		&client.RemoteExec{
			ExecParams: ssh.ExecParams{
				Command: "foo",
				Timeout: testing.LongWait,
			},
		},
	}

	runResults := client.ParallelExecute("/some/dir", params)
	c.Assert(runResults.Results, gc.HasLen, 1)
	result := runResults.Results[0]
	c.Assert(result.Error, gc.Equals, "missing host address")
}

func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) {
	s.mockSSH(c, echoInputShowArgs)

	params := []*client.RemoteExec{
		&client.RemoteExec{
			ExecParams: ssh.ExecParams{
				Host:    "localhost",
				Command: "foo",
				Timeout: testing.LongWait,
			},
		},
	}

	runResults := client.ParallelExecute("/some/dir", params)
	c.Assert(runResults.Results, gc.HasLen, 1)
	result := runResults.Results[0]
	c.Assert(result.Error, gc.Equals, "")
	c.Assert(string(result.Stderr), jc.Contains, "-i /some/dir/system-identity")
}

func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) {
	s.mockSSH(c, echoInputShowArgs)

	params := []*client.RemoteExec{
		&client.RemoteExec{
			ExecParams: ssh.ExecParams{
				Host:    "localhost",
				Command: "foo",
				Timeout: testing.LongWait,
			},
			MachineId: "machine-id",
			UnitId:    "unit-id",
		},
	}

	runResults := client.ParallelExecute("/some/dir", params)
	c.Assert(runResults.Results, gc.HasLen, 1)
	result := runResults.Results[0]
	c.Assert(result.Error, gc.Equals, "")
	c.Assert(result.MachineId, gc.Equals, "machine-id")
	c.Assert(result.UnitId, gc.Equals, "unit-id")
}

func (s *runSuite) TestRunOnAllMachines(c *gc.C) {
	// Make three machines.
	s.addMachineWithAddress(c, "10.3.2.1")
	s.addMachineWithAddress(c, "10.3.2.2")
	s.addMachineWithAddress(c, "10.3.2.3")

	s.mockSSH(c, echoInput)

	// hmm... this seems to be going through the api client, and from there
	// through to the apiserver implementation. Not ideal, but it is how the
	// other client tests are written.
	client := s.APIState.Client()
	results, err := client.RunOnAllMachines("hostname", testing.LongWait)
	c.Assert(err, gc.IsNil)
	c.Assert(results, gc.HasLen, 3)
	var expectedResults []params.RunResult
	for i := 0; i < 3; i++ {
		expectedResults = append(expectedResults,
			params.RunResult{
				ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")},
				MachineId:    fmt.Sprint(i),
			})
	}

	c.Assert(results, jc.DeepEquals, expectedResults)
}

func (s *runSuite) TestRunMachineAndService(c *gc.C) {
	// Make three machines.
	s.addMachineWithAddress(c, "10.3.2.1")

	charm := s.AddTestingCharm(c, "dummy")
	magic, err := s.State.AddService("magic", "user-admin", charm)
	s.addUnit(c, magic)
	s.addUnit(c, magic)

	s.mockSSH(c, echoInput)

	// hmm... this seems to be going through the api client, and from there
	// through to the apiserver implementation. Not ideal, but it is how the
	// other client tests are written.
	client := s.APIState.Client()
	results, err := client.Run(
		params.RunParams{
			Commands: "hostname",
			Timeout:  testing.LongWait,
			Machines: []string{"0"},
			Services: []string{"magic"},
		})
	c.Assert(err, gc.IsNil)
	c.Assert(results, gc.HasLen, 3)
	expectedResults := []params.RunResult{
		params.RunResult{
			ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")},
			MachineId:    "0",
		},
		params.RunResult{
			ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/0 'hostname'\n")},
			MachineId:    "1",
			UnitId:       "magic/0",
		},
		params.RunResult{
			ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/1 'hostname'\n")},
			MachineId:    "2",
			UnitId:       "magic/1",
		},
	}

	c.Assert(results, jc.DeepEquals, expectedResults)
}

var echoInputShowArgs = `#!/bin/bash
# Write the args to stderr
echo "$*" >&2
# And echo stdin to stdout
while read line
do echo $line
done <&0
`

var echoInput = `#!/bin/bash
# And echo stdin to stdout
while read line
do echo $line
done <&0
`
<commit_msg>Fix the test.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package client_test

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"time"

	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver/client"
	"launchpad.net/juju-core/testing"
	jc "launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/utils/exec"
	"launchpad.net/juju-core/utils/ssh"
)

type runSuite struct {
	baseSuite
}

var _ = gc.Suite(&runSuite{})

func (s *runSuite) addMachine(c *gc.C) *state.Machine {
	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
	c.Assert(err, gc.IsNil)
	return machine
}

func (s *runSuite) addMachineWithAddress(c *gc.C, address string) *state.Machine {
	machine := s.addMachine(c)
	machine.SetAddresses([]instance.Address{instance.NewAddress(address)})
	return machine
}

func (s *runSuite) TestRemoteParamsForMachinePopulates(c *gc.C) {
	machine := s.addMachine(c)
	result := client.RemoteParamsForMachine(machine, "command", time.Minute)
	c.Assert(result.Command, gc.Equals, "command")
	c.Assert(result.Timeout, gc.Equals, time.Minute)
	c.Assert(result.MachineId, gc.Equals, machine.Id())
	// Now an empty host isn't particularly useful, but the machine doesn't
	// have an address to use.
	c.Assert(machine.Addresses(), gc.HasLen, 0)
	c.Assert(result.Host, gc.Equals, "")
}

func (s *runSuite) TestRemoteParamsForMachinePopulatesWithAddress(c *gc.C) {
	machine := s.addMachineWithAddress(c, "10.3.2.1")

	result := client.RemoteParamsForMachine(machine, "command", time.Minute)
	c.Assert(result.Command, gc.Equals, "command")
	c.Assert(result.Timeout, gc.Equals, time.Minute)
	c.Assert(result.MachineId, gc.Equals, machine.Id())
	c.Assert(result.Host, gc.Equals, "ubuntu@10.3.2.1")
}

func (s *runSuite) addUnit(c *gc.C, service *state.Service) *state.Unit {
	unit, err := service.AddUnit()
	c.Assert(err, gc.IsNil)
	err = unit.AssignToNewMachine()
	c.Assert(err, gc.IsNil)
	mId, err := unit.AssignedMachineId()
	c.Assert(err, gc.IsNil)
	machine, err := s.State.Machine(mId)
	c.Assert(err, gc.IsNil)
	machine.SetAddresses([]instance.Address{instance.NewAddress("10.3.2.1")})
	return unit
}

func (s *runSuite) TestGetAllUnitNames(c *gc.C) {
	charm := s.AddTestingCharm(c, "dummy")
	magic, err := s.State.AddService("magic", "user-admin", charm)
	s.addUnit(c, magic)
	s.addUnit(c, magic)

	notAssigned, err := s.State.AddService("not-assigned", "user-admin", charm)
	c.Assert(err, gc.IsNil)
	_, err = notAssigned.AddUnit()
	c.Assert(err, gc.IsNil)

	_, err = s.State.AddService("no-units", "user-admin", charm)
	c.Assert(err, gc.IsNil)

	for i, test := range []struct {
		message  string
		expected []string
		units    []string
		services []string
		error    string
	}{{
		message: "no units, expected nil slice",
	}, {
		message: "asking for a unit that isn't there",
		units:   []string{"foo/0"},
		error:   `unit "foo/0" not found`,
	}, {
		message:  "asking for a service that isn't there",
		services: []string{"foo"},
		error:    `service "foo" not found`,
	}, {
		message:  "service with no units is not really an error",
		services: []string{"no-units"},
	}, {
		message:  "A service with units not assigned is an error",
		services: []string{"not-assigned"},
		error:    `unit "not-assigned/0" is not assigned to a machine`,
	}, {
		message:  "A service with units",
		services: []string{"magic"},
		expected: []string{"magic/0", "magic/1"},
	}, {
		message:  "Asking for just a unit",
		units:    []string{"magic/0"},
		expected: []string{"magic/0"},
	}, {
		message:  "Asking for a unit, and the service",
		services: []string{"magic"},
		units:    []string{"magic/0"},
		expected: []string{"magic/0", "magic/1"},
	}} {
		c.Logf("%v: %s", i, test.message)
		result, err := client.GetAllUnitNames(s.State, test.units, test.services)
		if test.error == "" {
			c.Check(err, gc.IsNil)
			var units []string
			for _, unit := range result {
				units = append(units, unit.Name())
			}
			c.Check(units, jc.SameContents, test.expected)
		} else {
			c.Check(err, gc.ErrorMatches, test.error)
		}
	}
}

func (s *runSuite) mockSSH(c *gc.C, cmd string) {
	testbin := c.MkDir()
	fakessh := filepath.Join(testbin, "ssh")
	newPath := testbin + ":" + os.Getenv("PATH")
	s.PatchEnvironment("PATH", newPath)
	err := ioutil.WriteFile(fakessh, []byte(cmd), 0755)
	c.Assert(err, gc.IsNil)
}

func (s *runSuite) TestParallelExecuteErrorsOnBlankHost(c *gc.C) {
	s.mockSSH(c, echoInputShowArgs)

	params := []*client.RemoteExec{
		&client.RemoteExec{
			ExecParams: ssh.ExecParams{
				Command: "foo",
				Timeout: testing.LongWait,
			},
		},
	}

	runResults := client.ParallelExecute("/some/dir", params)
	c.Assert(runResults.Results, gc.HasLen, 1)
	result := runResults.Results[0]
	c.Assert(result.Error, gc.Equals, "missing host address")
}

func (s *runSuite) TestParallelExecuteAddsIdentity(c *gc.C) {
	s.mockSSH(c, echoInputShowArgs)

	params := []*client.RemoteExec{
		&client.RemoteExec{
			ExecParams: ssh.ExecParams{
				Host:    "localhost",
				Command: "foo",
				Timeout: testing.LongWait,
			},
		},
	}

	runResults := client.ParallelExecute("/some/dir", params)
	c.Assert(runResults.Results, gc.HasLen, 1)
	result := runResults.Results[0]
	c.Assert(result.Error, gc.Equals, "")
	c.Assert(string(result.Stderr), jc.Contains, "-i /some/dir/system-identity")
}

func (s *runSuite) TestParallelExecuteCopiesAcrossMachineAndUnit(c *gc.C) {
	s.mockSSH(c, echoInputShowArgs)

	params := []*client.RemoteExec{
		&client.RemoteExec{
			ExecParams: ssh.ExecParams{
				Host:    "localhost",
				Command: "foo",
				Timeout: testing.LongWait,
			},
			MachineId: "machine-id",
			UnitId:    "unit-id",
		},
	}

	runResults := client.ParallelExecute("/some/dir", params)
	c.Assert(runResults.Results, gc.HasLen, 1)
	result := runResults.Results[0]
	c.Assert(result.Error, gc.Equals, "")
	c.Assert(result.MachineId, gc.Equals, "machine-id")
	c.Assert(result.UnitId, gc.Equals, "unit-id")
}

func (s *runSuite) TestRunOnAllMachines(c *gc.C) {
	// Make three machines.
	s.addMachineWithAddress(c, "10.3.2.1")
	s.addMachineWithAddress(c, "10.3.2.2")
	s.addMachineWithAddress(c, "10.3.2.3")

	s.mockSSH(c, echoInput)

	// hmm... this seems to be going through the api client, and from there
	// through to the apiserver implementation. Not ideal, but it is how the
	// other client tests are written.
	client := s.APIState.Client()
	results, err := client.RunOnAllMachines("hostname", testing.LongWait)
	c.Assert(err, gc.IsNil)
	c.Assert(results, gc.HasLen, 3)
	var expectedResults []params.RunResult
	for i := 0; i < 3; i++ {
		expectedResults = append(expectedResults,
			params.RunResult{
				ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run --no-context 'hostname'\n")},
				MachineId:    fmt.Sprint(i),
			})
	}

	c.Assert(results, jc.DeepEquals, expectedResults)
}

func (s *runSuite) TestRunMachineAndService(c *gc.C) {
	// Make three machines.
	s.addMachineWithAddress(c, "10.3.2.1")

	charm := s.AddTestingCharm(c, "dummy")
	magic, err := s.State.AddService("magic", "user-admin", charm)
	s.addUnit(c, magic)
	s.addUnit(c, magic)

	s.mockSSH(c, echoInput)

	// hmm... this seems to be going through the api client, and from there
	// through to the apiserver implementation. Not ideal, but it is how the
	// other client tests are written.
	client := s.APIState.Client()
	results, err := client.Run(
		params.RunParams{
			Commands: "hostname",
			Timeout:  testing.LongWait,
			Machines: []string{"0"},
			Services: []string{"magic"},
		})
	c.Assert(err, gc.IsNil)
	c.Assert(results, gc.HasLen, 3)
	expectedResults := []params.RunResult{
		params.RunResult{
			ExecResponse: exec.ExecResponse{Stdout: []byte("[ -f \"$HOME/.juju-proxy\" ] && . \"$HOME/.juju-proxy\"\njuju-run --no-context 'hostname'\n")},
			MachineId:    "0",
		},
		params.RunResult{
			ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/0 'hostname'\n")},
			MachineId:    "1",
			UnitId:       "magic/0",
		},
		params.RunResult{
			ExecResponse: exec.ExecResponse{Stdout: []byte("juju-run magic/1 'hostname'\n")},
			MachineId:    "2",
			UnitId:       "magic/1",
		},
	}

	c.Assert(results, jc.DeepEquals, expectedResults)
}

var echoInputShowArgs = `#!/bin/bash
# Write the args to stderr
echo "$*" >&2
# And echo stdin to stdout
while read line
do echo $line
done <&0
`

var echoInput = `#!/bin/bash
# And echo stdin to stdout
while read line
do echo $line
done <&0
`
<|endoftext|>


<commit_before><commit_msg>Print name conflicts.<commit_after><|endoftext|>


<commit_before>// Data structure for the OAI-PMH protocol request:
package oai

import "strings"

// Represents a request URL and query string to an OAI-PMH service
type Request struct {
	BaseUrl, Set, MetadataPrefix, Verb, Identifier, ResumptionToken, From, Until string
}

// String representation of the OAI Request
func (req *Request) String() string {
	qs := []string{}

	add := func(name, value string) {
		if value != "" {
			qs = append(qs, name + "=" + value)
		}
	}

	add("verb", req.Verb)
	add("set", req.Set)
	add("metadataPrefix", req.MetadataPrefix)
	add("resumptionToken", req.ResumptionToken)
	add("identifier", req.Identifier)
	add("from", req.From)
	add("until", req.Until)

	return strings.Join([]string{req.BaseUrl, "?", strings.Join(qs, "&")}, "")
}
<commit_msg>Run go fmt<commit_after>// Data structure for the OAI-PMH protocol request:
package oai

import "strings"

// Represents a request URL and query string to an OAI-PMH service
type Request struct {
	BaseUrl, Set, MetadataPrefix, Verb, Identifier, ResumptionToken, From, Until string
}

// String representation of the OAI Request
func (req *Request) String() string {
	qs := []string{}

	add := func(name, value string) {
		if value != "" {
			qs = append(qs, name+"="+value)
		}
	}

	add("verb", req.Verb)
	add("set", req.Set)
	add("metadataPrefix", req.MetadataPrefix)
	add("resumptionToken", req.ResumptionToken)
	add("identifier", req.Identifier)
	add("from", req.From)
	add("until", req.Until)

	return strings.Join([]string{req.BaseUrl, "?", strings.Join(qs, "&")}, "")
}
<|endoftext|>


<commit_before><commit_msg>repo contributions profile page<commit_after><|endoftext|>


<commit_before><commit_msg>Addresses issue #6596. Removes ForTesting_ReferencesAllowBlankSelfLinks. Fixes controller's persistentvolumes unit tests.<commit_after><|endoftext|>


<commit_before>package dbus

import "fmt"

func ExampleConn_Emit() {
	conn, err := ConnectSystemBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	if err := conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF)); err != nil {
		panic(err)
	}
}

func ExampleObject_Call() {
	var list []string

	conn, err := ConnectSessionBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list)
	if err != nil {
		panic(err)
	}
	for _, v := range list {
		fmt.Println(v)
	}
}

func ExampleObject_Go() {
	conn, err := ConnectSessionBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	ch := make(chan *Call, 10)
	conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch)
	select {
	case call := <-ch:
		if call.Err != nil {
			panic(err)
		}
		list := call.Body[0].([]string)
		for _, v := range list {
			fmt.Println(v)
		}
		// put some other cases here
	}
}
<commit_msg>examples_test: fix gosimple warning<commit_after>package dbus

import "fmt"

func ExampleConn_Emit() {
	conn, err := ConnectSystemBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	if err := conn.Emit("/foo/bar", "foo.bar.Baz", uint32(0xDAEDBEEF)); err != nil {
		panic(err)
	}
}

func ExampleObject_Call() {
	var list []string

	conn, err := ConnectSessionBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	err = conn.BusObject().Call("org.freedesktop.DBus.ListNames", 0).Store(&list)
	if err != nil {
		panic(err)
	}
	for _, v := range list {
		fmt.Println(v)
	}
}

func ExampleObject_Go() {
	conn, err := ConnectSessionBus()
	if err != nil {
		panic(err)
	}
	defer conn.Close()

	ch := make(chan *Call, 10)
	conn.BusObject().Go("org.freedesktop.DBus.ListActivatableNames", 0, ch)
	call := <-ch
	if call.Err != nil {
		panic(err)
	}
	list := call.Body[0].([]string)
	for _, v := range list {
		fmt.Println(v)
	}
}
<|endoftext|>


<commit_before>package jni

import (
	"reflect"
	"testing"

	"veyron2/vdl/test_base"
)

func init() {
	registerInterface((*test_base.ServiceB)(nil))
}

func compareType(t *testing.T, method string, got, want interface{}, argKind string) {
	if gotT, wantT := reflect.TypeOf(got), reflect.TypeOf(want); gotT != wantT {
		t.Errorf("type mismatch in %q's %s argument: got %v , want %v ", method, argKind, gotT, wantT)
	}
}

func compareTypes(t *testing.T, method string, got, want []interface{}, argKind string) {
	if len(got) != len(want) {
		t.Errorf("mismatch in input arguments: got %v , want %v ", got, want)
	}
	for i, _ := range got {
		compareType(t, method, got[i], want[i], argKind)
	}
}

func TestGetter(t *testing.T) {
	iface := "veyron2/vdl/test_base/ServiceB"
	getter := newArgGetter(iface)
	if getter == nil {
		t.Fatalf("no getter for interface: %v ", iface)
	}
	if got, want := getter.vdlPath, iface; got != want {
		t.Errorf("invalid pathname: got %v , want %v ", got, want)
	}
	data := []struct {
		Method       string
		NumInArgs    int
		in, out      []interface{}
		sSend, sRecv interface{}
		sFinish      []interface{}
	}{
		{"MethodA1", 0, nil, nil, nil, nil, nil},
		{"MethodA2", 2, []interface{}{(*int32)(nil), (*string)(nil)}, []interface{}{(*string)(nil)}, nil, nil, nil},
		{"MethodA3", 1, []interface{}{(*int32)(nil)}, nil, nil, (*test_base.Scalars)(nil), []interface{}{(*string)(nil)}},
		{"MethodA4", 1, []interface{}{(*int32)(nil)}, nil, (*int32)(nil), (*string)(nil), nil},
		{"MethodB1", 2, []interface{}{(*test_base.Scalars)(nil), (*test_base.Composites)(nil)}, []interface{}{(*test_base.CompComp)(nil)}, nil, nil, nil},
	}
	for _, d := range data {
		m := getter.FindMethod(d.Method, d.NumInArgs)
		if m == nil {
			t.Errorf("couldn't find method %q with %d args", d.Method, d.NumInArgs)
			continue
		}
		// Compare arguments.
		compareTypes(t, d.Method, m.InPtrs(), d.in, "input")
		compareTypes(t, d.Method, m.OutPtrs(), d.out, "output")
		compareType(t, d.Method, m.StreamSendPtr(), d.sSend, "stream send")
		compareType(t, d.Method, m.StreamRecvPtr(), d.sRecv, "stream recv")
		compareTypes(t, d.Method, m.StreamFinishPtrs(), d.sFinish, "stream finish")
	}
}
<commit_msg>veyron/runtimes/google/ipc/jni: Fix broken test build.<commit_after>package jni

import (
	"reflect"
	"testing"

	"veyron2/vdl/test_base"
)

func init() {
	registerInterface((*test_base.ServiceB)(nil))
}

func compareType(t *testing.T, method string, got, want interface{}, argKind string) {
	if gotT, wantT := reflect.TypeOf(got), reflect.TypeOf(want); gotT != wantT {
		t.Errorf("type mismatch in %q's %s argument: got %v , want %v ", method, argKind, gotT, wantT)
	}
}

func compareTypes(t *testing.T, method string, got, want []interface{}, argKind string) {
	if len(got) != len(want) {
		t.Errorf("mismatch in input arguments: got %v , want %v ", got, want)
	}
	for i, _ := range got {
		compareType(t, method, got[i], want[i], argKind)
	}
}

func TestGetter(t *testing.T) {
	iface := "veyron2/vdl/test_base/ServiceB"
	getter, err := newArgGetter([]string{iface})
	if err != nil {
		t.Fatalf("couldn't find getter for interface: %v ", iface)
	}
	data := []struct {
		Method       string
		NumInArgs    int
		in, out      []interface{}
		sSend, sRecv interface{}
		sFinish      []interface{}
	}{
		{"MethodA1", 0, nil, nil, nil, nil, nil},
		{"MethodA2", 2, []interface{}{(*int32)(nil), (*string)(nil)}, []interface{}{(*string)(nil)}, nil, nil, nil},
		{"MethodA3", 1, []interface{}{(*int32)(nil)}, nil, nil, (*test_base.Scalars)(nil), []interface{}{(*string)(nil)}},
		{"MethodA4", 1, []interface{}{(*int32)(nil)}, nil, (*int32)(nil), (*string)(nil), nil},
		{"MethodB1", 2, []interface{}{(*test_base.Scalars)(nil), (*test_base.Composites)(nil)}, []interface{}{(*test_base.CompComp)(nil)}, nil, nil, nil},
	}
	for _, d := range data {
		m := getter.FindMethod(d.Method, d.NumInArgs)
		if m == nil {
			t.Errorf("couldn't find method %q with %d args", d.Method, d.NumInArgs)
			continue
		}
		// Compare arguments.
		compareTypes(t, d.Method, m.InPtrs(), d.in, "input")
		compareTypes(t, d.Method, m.OutPtrs(), d.out, "output")
		compareType(t, d.Method, m.StreamSendPtr(), d.sSend, "stream send")
		compareType(t, d.Method, m.StreamRecvPtr(), d.sRecv, "stream recv")
		compareTypes(t, d.Method, m.StreamFinishPtrs(), d.sFinish, "stream finish")
	}
}
<|endoftext|>