<commit_before>package expect

import (
	"runtime"

	"github.com/apoydence/onpar/matchers"
)

// T is a type that we can perform assertions with.
type T interface {
	Fatalf(format string, args ...interface{})
}

type Expectation func(actual interface{}) *To

func New(t T) Expectation {
	return func(actual interface{}) *To {
		return &To{
			actual: actual,
			t:      t,
		}
	}
}

func Expect(t T, actual interface{}) *To {
	return &To{
		actual: actual,
		t:      t,
	}
}

type To struct {
	actual    interface{}
	parentErr error

	t T
}

func (t *To) To(matcher matchers.Matcher) {
	_, err := matcher.Match(t.actual)
	if err != nil {
		_, fileName, lineNumber, _ := runtime.Caller(1)
		t.t.Fatalf("%s\n%s:%d", err.Error(), fileName, lineNumber)
	}
}
<commit_msg>Expect will try to declare To as a test helper<commit_after>package expect

import (
	"runtime"

	"github.com/apoydence/onpar/matchers"
)

// T is a type that we can perform assertions with.
type T interface {
	Fatalf(format string, args ...interface{})
}

// THelper has the method that tells the testing framework that it can declare
// itself a test helper.
type THelper interface {
	Helper()
}

type Expectation func(actual interface{}) *To

func New(t T) Expectation {
	return func(actual interface{}) *To {
		return &To{
			actual: actual,
			t:      t,
		}
	}
}

func Expect(t T, actual interface{}) *To {
	return &To{
		actual: actual,
		t:      t,
	}
}

type To struct {
	actual    interface{}
	parentErr error

	t T
}

func (t *To) To(matcher matchers.Matcher) {
	if helper, ok := t.t.(THelper); ok {
		helper.Helper()
	}

	_, err := matcher.Match(t.actual)
	if err != nil {
		_, fileName, lineNumber, _ := runtime.Caller(1)
		t.t.Fatalf("%s\n%s:%d", err.Error(), fileName, lineNumber)
	}
}
<|endoftext|>


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

// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package hplot

import (
	"math"

	"golang.org/x/exp/shiny/screen"

	"github.com/go-hep/hplot/vgshiny"
	"github.com/gonum/plot"
	"github.com/gonum/plot/vg"
	"github.com/gonum/plot/vg/draw"
	"github.com/gonum/plot/vg/vgimg"
)

// Plot is the basic type representing a plot.
type Plot struct {
	plot.Plot
}

// New returns a new plot with some reasonable
// default settings.
func New() (*Plot, error) {
	p, err := plot.New()
	if err != nil {
		return nil, err
	}
	// p.X.Padding = 0
	// p.Y.Padding = 0
	// p.Style = GnuplotStyle{}
	return &Plot{*p}, nil
}

// Add adds a Plotters to the plot.
//
// If the plotters implements DataRanger then the
// minimum and maximum values of the X and Y
// axes are changed if necessary to fit the range of
// the data.
//
// When drawing the plot, Plotters are drawn in the
// order in which they were added to the plot.
func (p *Plot) Add(ps ...plot.Plotter) {
	for _, d := range ps {
		if x, ok := d.(plot.DataRanger); ok {
			xmin, xmax, ymin, ymax := x.DataRange()
			p.Plot.X.Min = math.Min(p.Plot.X.Min, xmin)
			p.Plot.X.Max = math.Max(p.Plot.X.Max, xmax)
			p.Plot.Y.Min = math.Min(p.Plot.Y.Min, ymin)
			p.Plot.Y.Max = math.Max(p.Plot.Y.Max, ymax)
		}
	}

	p.Plot.Add(ps...)
}

// Save saves the plot to an image file.  The file format is determined
// by the extension.
//
// Supported extensions are:
//
//  .eps, .jpg, .jpeg, .pdf, .png, .svg, .tif and .tiff.
//
// If w or h are <= 0, the value is chosen such that it follows the Golden Ratio.
// If w and h are <= 0, the values are chosen such that they follow the Golden Ratio
// (the width is defaulted to vgimg.DefaultWidth).
func (p *Plot) Save(w, h vg.Length, file string) (err error) {
	switch {
	case w <= 0 && h <= 0:
		w = vgimg.DefaultWidth
		h = vgimg.DefaultWidth / math.Phi
	case w <= 0:
		w = h * math.Phi
	case h <= 0:
		h = w / math.Phi
	}
	return p.Plot.Save(w, h, file)
}

// Show displays the plot to the screen, with the given dimensions.
//
// If w or h are <= 0, the value is chosen such that it follows the Golden Ratio.
// If w and h are <= 0, the values are chosen such that they follow the Golden Ratio
// (the width is defaulted to vgimg.DefaultWidth).
func (p *Plot) Show(w, h vg.Length, scr screen.Screen) (*vgshiny.Canvas, error) {
	switch {
	case w <= 0 && h <= 0:
		w = vgimg.DefaultWidth
		h = vgimg.DefaultWidth / math.Phi
	case w <= 0:
		w = h * math.Phi
	case h <= 0:
		h = w / math.Phi
	}
	c, err := vgshiny.New(scr, w, h)
	if err != nil {
		return nil, err
	}
	p.Draw(draw.New(c))
	c.Paint()
	return c, err
}
<commit_msg>hplot: add ZipXY<commit_after>// Copyright ©2016 The go-hep Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Copyright 2012 The Plotinum Authors. All rights reserved.
// Use of this source code is governed by an MIT-style license
// that can be found in the LICENSE file.

package hplot

import (
	"math"

	"golang.org/x/exp/shiny/screen"

	"github.com/go-hep/hplot/vgshiny"
	"github.com/gonum/plot"
	"github.com/gonum/plot/plotter"
	"github.com/gonum/plot/vg"
	"github.com/gonum/plot/vg/draw"
	"github.com/gonum/plot/vg/vgimg"
)

// Plot is the basic type representing a plot.
type Plot struct {
	plot.Plot
}

// New returns a new plot with some reasonable
// default settings.
func New() (*Plot, error) {
	p, err := plot.New()
	if err != nil {
		return nil, err
	}
	// p.X.Padding = 0
	// p.Y.Padding = 0
	// p.Style = GnuplotStyle{}
	return &Plot{*p}, nil
}

// Add adds a Plotters to the plot.
//
// If the plotters implements DataRanger then the
// minimum and maximum values of the X and Y
// axes are changed if necessary to fit the range of
// the data.
//
// When drawing the plot, Plotters are drawn in the
// order in which they were added to the plot.
func (p *Plot) Add(ps ...plot.Plotter) {
	for _, d := range ps {
		if x, ok := d.(plot.DataRanger); ok {
			xmin, xmax, ymin, ymax := x.DataRange()
			p.Plot.X.Min = math.Min(p.Plot.X.Min, xmin)
			p.Plot.X.Max = math.Max(p.Plot.X.Max, xmax)
			p.Plot.Y.Min = math.Min(p.Plot.Y.Min, ymin)
			p.Plot.Y.Max = math.Max(p.Plot.Y.Max, ymax)
		}
	}

	p.Plot.Add(ps...)
}

// Save saves the plot to an image file.  The file format is determined
// by the extension.
//
// Supported extensions are:
//
//  .eps, .jpg, .jpeg, .pdf, .png, .svg, .tif and .tiff.
//
// If w or h are <= 0, the value is chosen such that it follows the Golden Ratio.
// If w and h are <= 0, the values are chosen such that they follow the Golden Ratio
// (the width is defaulted to vgimg.DefaultWidth).
func (p *Plot) Save(w, h vg.Length, file string) (err error) {
	switch {
	case w <= 0 && h <= 0:
		w = vgimg.DefaultWidth
		h = vgimg.DefaultWidth / math.Phi
	case w <= 0:
		w = h * math.Phi
	case h <= 0:
		h = w / math.Phi
	}
	return p.Plot.Save(w, h, file)
}

// Show displays the plot to the screen, with the given dimensions.
//
// If w or h are <= 0, the value is chosen such that it follows the Golden Ratio.
// If w and h are <= 0, the values are chosen such that they follow the Golden Ratio
// (the width is defaulted to vgimg.DefaultWidth).
func (p *Plot) Show(w, h vg.Length, scr screen.Screen) (*vgshiny.Canvas, error) {
	switch {
	case w <= 0 && h <= 0:
		w = vgimg.DefaultWidth
		h = vgimg.DefaultWidth / math.Phi
	case w <= 0:
		w = h * math.Phi
	case h <= 0:
		h = w / math.Phi
	}
	c, err := vgshiny.New(scr, w, h)
	if err != nil {
		return nil, err
	}
	p.Draw(draw.New(c))
	c.Paint()
	return c, err
}

// zip zips together 2 slices and implements the plotter.XYer interface.
type zip struct {
	x []float64
	y []float64
}

// Len implements the plotter.XYer interface
func (z zip) Len() int { return len(z.x) }

// XY implements the plotter.XYer interface
func (z zip) XY(i int) (x, y float64) { return z.x[i], z.y[i] }

// ZipXY zips together 2 slices x and y in such a way to implement the
// plotter.XYer interface.
func ZipXY(x, y []float64) plotter.XYer {
	return zip{x: x, y: y}
}
<|endoftext|>


<commit_before>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

package id3v2

//go:generate go run generate_ids.go

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"sync"
)

var (
	errIncompleteID3 = errors.New("id3: incomplete tag block")
	errInvalidID3    = errors.New("id3: invalid tag data")
)

const (
	flagUnsynchronisation = 1 << (7 - iota)
	flagExtendedHeader
	flagExperimental
	flagFooter
)

type FrameID uint32

func syncsafe(data []byte) (uint32, error) {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return 0, errInvalidID3
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3]), nil
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, errIncompleteID3
		}

		return i, nil, nil
	}

	if data[3] == 0xff || data[4] == 0xff {
		return 0, nil, errInvalidID3
	}

	size, err := syncsafe(data[6:])
	if err != nil {
		return 0, nil, err
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, errIncompleteID3
		}

		return i, nil, nil
	}

	if data[5]&flagFooter == flagFooter {
		size += 10
	}

	return i + 10 + int(size), data[:10+size], nil
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

func Scan(r io.Reader) (ID3Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), bufio.MaxScanTokenSize)
	s.Split(id3Split)

	var frames ID3Frames

scan:
	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		switch header[3] {
		case 0x04:
		default:
			continue scan
		}

		flags := header[5]
		if flags&flagExtendedHeader == flagExtendedHeader {
			size, err := syncsafe(data)
			if err != nil {
				return nil, err
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

		if flags&flagFooter == flagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errInvalidID3
			}
		}

		for len(data) > 10 {
			_ = data[9]

			id := FrameID(data[0])<<24 | FrameID(data[1])<<16 |
				FrameID(data[2])<<8 | FrameID(data[3])

			size, err := syncsafe(data[4:])
			if err != nil {
				return nil, err
			}

			if len(data) < 10+int(size) {
				return nil, errInvalidID3
			}

			frames = append(frames, &ID3Frame{
				ID:    id,
				Flags: uint16(data[8])<<8 | uint16(data[9]),
				Data:  append([]byte(nil), data[10:10+size]...),
			})

			data = data[10+size:]
		}

		if flags&flagFooter == flagFooter && len(data) != 0 {
			return nil, errInvalidID3
		}

		for _, v := range data {
			if v != 0 {
				return nil, errInvalidID3
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

type ID3Frames []*ID3Frame

func (frames ID3Frames) Lookup(id FrameID) *ID3Frame {
	for _, frame := range frames {
		if frame.ID == id {
			return frame
		}
	}

	return nil
}

type ID3Frame struct {
	ID    FrameID
	Flags uint16
	Data  []byte
}

func (f *ID3Frame) String() string {
	return fmt.Sprintf("&ID3Frame{ID: %s, Flags: %04x, Data: [%d]byte{...}}",
		f.ID.String(), f.Flags, len(f.Data))
}
<commit_msg>Add reference to id3v2.4.0-structure.txt<commit_after>// Copyright 2017 Tom Thorogood. All rights reserved.
// Use of this source code is governed by a Modified
// BSD License that can be found in the LICENSE file.

package id3v2

//go:generate go run generate_ids.go

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"sync"
)

// This is an implementation of v2.4.0 of the ID3v2 tagging format,
// defined in: http://id3.org/id3v2.4.0-structure.

var (
	errIncompleteID3 = errors.New("id3: incomplete tag block")
	errInvalidID3    = errors.New("id3: invalid tag data")
)

const (
	flagUnsynchronisation = 1 << (7 - iota)
	flagExtendedHeader
	flagExperimental
	flagFooter
)

type FrameID uint32

func syncsafe(data []byte) (uint32, error) {
	_ = data[3]

	if data[0]&0x80 != 0 || data[1]&0x80 != 0 ||
		data[2]&0x80 != 0 || data[3]&0x80 != 0 {
		return 0, errInvalidID3
	}

	return uint32(data[0])<<21 | uint32(data[1])<<14 |
		uint32(data[2])<<7 | uint32(data[3]), nil
}

func id3Split(data []byte, atEOF bool) (advance int, token []byte, err error) {
	i := bytes.Index(data, []byte("ID3"))
	if i == -1 {
		if len(data) < 2 {
			return 0, nil, nil
		}

		return len(data) - 2, nil, nil
	}

	data = data[i:]
	if len(data) < 10 {
		if atEOF {
			return 0, nil, errIncompleteID3
		}

		return i, nil, nil
	}

	if data[3] == 0xff || data[4] == 0xff {
		return 0, nil, errInvalidID3
	}

	size, err := syncsafe(data[6:])
	if err != nil {
		return 0, nil, err
	}

	if len(data) < 10+int(size) {
		if atEOF {
			return 0, nil, errIncompleteID3
		}

		return i, nil, nil
	}

	if data[5]&flagFooter == flagFooter {
		size += 10
	}

	return i + 10 + int(size), data[:10+size], nil
}

var bufPool = &sync.Pool{
	New: func() interface{} {
		buf := make([]byte, 4<<10)
		return &buf
	},
}

func Scan(r io.Reader) (ID3Frames, error) {
	buf := bufPool.Get()
	defer bufPool.Put(buf)

	s := bufio.NewScanner(r)
	s.Buffer(*buf.(*[]byte), bufio.MaxScanTokenSize)
	s.Split(id3Split)

	var frames ID3Frames

scan:
	for s.Scan() {
		data := s.Bytes()

		header := data[:10]
		data = data[10:]

		if string(header[:3]) != "ID3" {
			panic("id3: bufio.Scanner failed")
		}

		switch header[3] {
		case 0x04:
		default:
			continue scan
		}

		flags := header[5]
		if flags&flagExtendedHeader == flagExtendedHeader {
			size, err := syncsafe(data)
			if err != nil {
				return nil, err
			}

			extendedHeader := data[:size]
			data = data[size:]

			_ = extendedHeader
		}

		if flags&flagFooter == flagFooter {
			footer := data[len(data)-10:]
			data = data[:len(data)-10]

			if string(footer[:3]) != "3DI" ||
				!bytes.Equal(header[3:], footer[3:]) {
				return nil, errInvalidID3
			}
		}

		for len(data) > 10 {
			_ = data[9]

			id := FrameID(data[0])<<24 | FrameID(data[1])<<16 |
				FrameID(data[2])<<8 | FrameID(data[3])

			size, err := syncsafe(data[4:])
			if err != nil {
				return nil, err
			}

			if len(data) < 10+int(size) {
				return nil, errInvalidID3
			}

			frames = append(frames, &ID3Frame{
				ID:    id,
				Flags: uint16(data[8])<<8 | uint16(data[9]),
				Data:  append([]byte(nil), data[10:10+size]...),
			})

			data = data[10+size:]
		}

		if flags&flagFooter == flagFooter && len(data) != 0 {
			return nil, errInvalidID3
		}

		for _, v := range data {
			if v != 0 {
				return nil, errInvalidID3
			}
		}
	}

	if s.Err() != nil {
		return nil, s.Err()
	}

	return frames, nil
}

type ID3Frames []*ID3Frame

func (frames ID3Frames) Lookup(id FrameID) *ID3Frame {
	for _, frame := range frames {
		if frame.ID == id {
			return frame
		}
	}

	return nil
}

type ID3Frame struct {
	ID    FrameID
	Flags uint16
	Data  []byte
}

func (f *ID3Frame) String() string {
	return fmt.Sprintf("&ID3Frame{ID: %s, Flags: %04x, Data: [%d]byte{...}}",
		f.ID.String(), f.Flags, len(f.Data))
}
<|endoftext|>


<commit_before>package gcode

import "fmt"
import "errors"
import "strconv"

// Parses a string, and returns an AST.
func Parse(input string) (doc *Document, err error) {

	const (
		normal     = iota
		comment    = iota
		eolcomment = iota
		word       = iota
	)

	var (
		document    Document
		curBlock    Block = Block{}
		state       int   = normal
		lastNewline int   = 0
		buffer      string
		address     rune
	)

	input += "\n"

	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("%s", r))
		}
	}()

	parserPanic := func(idx int, err string) string {
		nl := 0
		for idy, s := range input {
			if idy == idx {
				break
			} else if s == '\n' {
				nl++
			}
		}
		panic(fmt.Sprintf("Line %d, pos %d: %s", nl, idx-lastNewline+1, err))
	}

	parseNormal := func(c rune, idx int) {
		switch c {
		case '/':
			if idx-lastNewline == 0 {
				curBlock.BlockDelete = true
				lastNewline--
			} else {
				parserPanic(idx, "Unexpected /")
			}
		case '%':
			fm := Filemarker{}
			curBlock.AppendNode(&fm)
		case '(':
			state = comment
		case ';':
			state = eolcomment
		case '\n':
			document.AppendBlock(curBlock)
			curBlock = Block{}
			lastNewline = idx + 1
		case ' ':
			// Ignore
			return
		default:
			if c >= 97 && c <= 122 {
				// Lower-case character
				state = word
				address = c - 32 // Make uppercase
			} else if (c >= 65 && c <= 90) || c == 64 || c == 94 {
				// Upper-case character, @ or ^
				state = word
				address = c
			} else {
				// No clue
				parserPanic(idx, fmt.Sprintf("Expected word address, found %c", c))
			}
		}
	}

	parseComment := func(c rune, idx int) {
		switch c {
		case ')':
			state = normal
			cm := Comment{buffer, false}
			curBlock.AppendNode(&cm)
			buffer = ""
		case '\n':
			parserPanic(idx, "Non-terminated comment")
		default:
			buffer += string(c)
		}
	}

	parseEOLComment := func(c rune, idx int) {
		switch c {
		case '\n':
			state = normal
			cm := Comment{buffer, true}
			curBlock.AppendNode(&cm)
			buffer = ""
			parseNormal(c, idx)
		default:
			buffer += string(c)
		}
	}

	parseWord := func(c rune, idx int) {
		if (c >= 48 && c <= 57) || c == 46 || c == 45 || c == 43 {
			// [0-9\.\-\+]
			buffer += string(c)
		} else {
			// End of command
			state = normal
			f, _ := strconv.ParseFloat(string(buffer), 64)
			w := Word{address, f}
			curBlock.AppendNode(&w)
			buffer = ""
			parseNormal(c, idx)
		}
	}

	for idx, c := range input {
		switch state {
		case normal:
			parseNormal(c, idx)
		case comment:
			parseComment(c, idx)
		case eolcomment:
			parseEOLComment(c, idx)
		case word:
			parseWord(c, idx)
		}
	}
	return &document, nil
}
<commit_msg>Parser fix<commit_after>package gcode

import "fmt"
import "errors"
import "strconv"

// Parses a string, and returns an AST.
func Parse(input string) (doc *Document, err error) {

	const (
		normal     = iota
		comment    = iota
		eolcomment = iota
		word       = iota
	)

	var (
		document    Document
		curBlock    Block = Block{}
		state       int   = normal
		lastNewline int   = 0
		buffer      string
		address     rune
	)

	input += "\n"

	defer func() {
		if r := recover(); r != nil {
			err = errors.New(fmt.Sprintf("%s", r))
		}
	}()

	parserPanic := func(idx int, err string) string {
		nl := 0
		for idy, s := range input {
			if idy == idx {
				break
			} else if s == '\n' {
				nl++
			}
		}
		panic(fmt.Sprintf("Line %d, pos %d: %s", nl, idx-lastNewline+1, err))
	}

	parseNormal := func(c rune, idx int) {
		switch c {
		case '/':
			if idx-lastNewline == 0 {
				curBlock.BlockDelete = true
				lastNewline--
			} else {
				parserPanic(idx, "Unexpected /")
			}
		case '%':
			fm := Filemarker{}
			curBlock.AppendNode(&fm)
		case '(':
			state = comment
		case ';':
			state = eolcomment
		case '\n':
			document.AppendBlock(curBlock)
			curBlock = Block{}
			lastNewline = idx + 1
		case '\r':
			// Ignore
			return
		case ' ':
			// Ignore
			return
		default:
			if c >= 97 && c <= 122 {
				// Lower-case character
				state = word
				address = c - 32 // Make uppercase
			} else if (c >= 65 && c <= 90) || c == 64 || c == 94 {
				// Upper-case character, @ or ^
				state = word
				address = c
			} else {
				// No clue
				parserPanic(idx, fmt.Sprintf("Expected word address, found [%c]", c))
			}
		}
	}

	parseComment := func(c rune, idx int) {
		switch c {
		case ')':
			state = normal
			cm := Comment{buffer, false}
			curBlock.AppendNode(&cm)
			buffer = ""
		case '\n':
			parserPanic(idx, "Non-terminated comment")
		default:
			buffer += string(c)
		}
	}

	parseEOLComment := func(c rune, idx int) {
		switch c {
		case '\n':
			state = normal
			cm := Comment{buffer, true}
			curBlock.AppendNode(&cm)
			buffer = ""
			parseNormal(c, idx)
		default:
			buffer += string(c)
		}
	}

	parseWord := func(c rune, idx int) {
		if (c >= 48 && c <= 57) || c == 46 || c == 45 || c == 43 {
			// [0-9\.\-\+]
			buffer += string(c)
		} else {
			// End of command
			state = normal
			f, _ := strconv.ParseFloat(string(buffer), 64)
			w := Word{address, f}
			curBlock.AppendNode(&w)
			buffer = ""
			parseNormal(c, idx)
		}
	}

	for idx, c := range input {
		switch state {
		case normal:
			parseNormal(c, idx)
		case comment:
			parseComment(c, idx)
		case eolcomment:
			parseEOLComment(c, idx)
		case word:
			parseWord(c, idx)
		}
	}
	return &document, nil
}
<|endoftext|>


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

package main

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

var cmdClean = &Command{
	UsageLine: "clean [-i] [-r] [-n] [-x] [packages]",
	Short:     "remove object files",
	Long: `
Clean removes object files from package source directories.
The go command builds most objects in a temporary directory,
so go clean is mainly concerned with object files left by other
tools or by manual invocations of go build.

Specifically, clean removes the following files from each of the
source directories corresponding to the import paths:

	_obj/            old object directory, left from Makefiles
	_test/           old test directory, left from Makefiles
	_testmain.go     old gotest file, left from Makefiles
	test.out         old test log, left from Makefiles
	build.out        old test log, left from Makefiles
	*.[568ao]        object files, left from Makefiles

	DIR(.exe)        from go build
	DIR.test(.exe)   from go test -c
	MAINFILE(.exe)   from go build MAINFILE.go
	*.so             from SWIG

In the list, DIR represents the final path element of the
directory, and MAINFILE is the base name of any Go source
file in the directory that is not included when building
the package.

The -i flag causes clean to remove the corresponding installed
archive or binary (what 'go install' would create).

The -n flag causes clean to print the remove commands it would execute,
but not run them.

The -r flag causes clean to be applied recursively to all the
dependencies of the packages named by the import paths.

The -x flag causes clean to print remove commands as it executes them.

For more about specifying packages, see 'go help packages'.
	`,
}

var cleanI bool // clean -i flag
var cleanN bool // clean -n flag
var cleanR bool // clean -r flag
var cleanX bool // clean -x flag

func init() {
	// break init cycle
	cmdClean.Run = runClean

	cmdClean.Flag.BoolVar(&cleanI, "i", false, "")
	cmdClean.Flag.BoolVar(&cleanN, "n", false, "")
	cmdClean.Flag.BoolVar(&cleanR, "r", false, "")
	cmdClean.Flag.BoolVar(&cleanX, "x", false, "")
}

func runClean(cmd *Command, args []string) {
	for _, pkg := range packagesAndErrors(args) {
		clean(pkg)
	}
}

var cleaned = map[*Package]bool{}

// TODO: These are dregs left by Makefile-based builds.
// Eventually, can stop deleting these.
var cleanDir = map[string]bool{
	"_test": true,
	"_obj":  true,
}

var cleanFile = map[string]bool{
	"_testmain.go": true,
	"test.out":     true,
	"build.out":    true,
	"a.out":        true,
}

var cleanExt = map[string]bool{
	".5":  true,
	".6":  true,
	".8":  true,
	".a":  true,
	".o":  true,
	".so": true,
}

func clean(p *Package) {
	if cleaned[p] {
		return
	}
	if p.Dir == "" {
		errorf("can't load package: %v", p.Error)
		return
	}
	dirs, err := ioutil.ReadDir(p.Dir)
	if err != nil {
		errorf("go clean %s: %v", p.Dir, err)
		return
	}

	var b builder
	b.print = fmt.Print

	packageFile := map[string]bool{}
	if p.Name != "main" {
		// Record which files are not in package main.
		// The others are.
		keep := func(list []string) {
			for _, f := range list {
				packageFile[f] = true
			}
		}
		keep(p.GoFiles)
		keep(p.CgoFiles)
		keep(p.TestGoFiles)
		keep(p.XTestGoFiles)
	}

	_, elem := filepath.Split(p.Dir)
	allRemove := []string{
		elem,
		elem + ".exe",
		elem + ".test",
		elem + ".test.exe",
	}
	for _, dir := range dirs {
		name := dir.Name()
		if packageFile[name] {
			continue
		}
		if !dir.IsDir() && strings.HasSuffix(name, ".go") {
			base := name[:len(name)-len(".go")]
			allRemove = append(allRemove, base, base+".exe")
		}
	}
	if cleanN || cleanX {
		b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
	}

	toRemove := map[string]bool{}
	for _, name := range allRemove {
		toRemove[name] = true
	}
	for _, dir := range dirs {
		name := dir.Name()
		if dir.IsDir() {
			// TODO: Remove once Makefiles are forgotten.
			if cleanDir[name] {
				if cleanN || cleanX {
					b.showcmd(p.Dir, "rm -r %s", name)
					if cleanN {
						continue
					}
				}
				os.RemoveAll(filepath.Join(p.Dir, name))
			}
			continue
		}

		if cleanN {
			continue
		}

		if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] {
			os.Remove(filepath.Join(p.Dir, name))
		}
	}

	if cleanI && p.target != "" {
		if cleanN || cleanX {
			b.showcmd("", "rm -f %s", p.target)
		}
		if !cleanN {
			os.Remove(p.target)
		}
	}

	if cleanI && p.usesSwig() {
		for _, f := range stringList(p.SwigFiles, p.SwigCXXFiles) {
			dir := p.swigDir(&buildContext)
			soname := p.swigSoname(f)
			target := filepath.Join(dir, soname)
			if cleanN || cleanX {
				b.showcmd("", "rm -f %s", target)
			}
			if !cleanN {
				os.Remove(target)
			}
		}
	}

	if cleanR {
		for _, p1 := range p.imports {
			clean(p1)
		}
	}
}
<commit_msg>cmd/go: don't ignore error when 'go clean'         Fixes issue 4208.<commit_after>// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

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

var cmdClean = &Command{
	UsageLine: "clean [-i] [-r] [-n] [-x] [packages]",
	Short:     "remove object files",
	Long: `
Clean removes object files from package source directories.
The go command builds most objects in a temporary directory,
so go clean is mainly concerned with object files left by other
tools or by manual invocations of go build.

Specifically, clean removes the following files from each of the
source directories corresponding to the import paths:

	_obj/            old object directory, left from Makefiles
	_test/           old test directory, left from Makefiles
	_testmain.go     old gotest file, left from Makefiles
	test.out         old test log, left from Makefiles
	build.out        old test log, left from Makefiles
	*.[568ao]        object files, left from Makefiles

	DIR(.exe)        from go build
	DIR.test(.exe)   from go test -c
	MAINFILE(.exe)   from go build MAINFILE.go
	*.so             from SWIG

In the list, DIR represents the final path element of the
directory, and MAINFILE is the base name of any Go source
file in the directory that is not included when building
the package.

The -i flag causes clean to remove the corresponding installed
archive or binary (what 'go install' would create).

The -n flag causes clean to print the remove commands it would execute,
but not run them.

The -r flag causes clean to be applied recursively to all the
dependencies of the packages named by the import paths.

The -x flag causes clean to print remove commands as it executes them.

For more about specifying packages, see 'go help packages'.
	`,
}

var cleanI bool // clean -i flag
var cleanN bool // clean -n flag
var cleanR bool // clean -r flag
var cleanX bool // clean -x flag

func init() {
	// break init cycle
	cmdClean.Run = runClean

	cmdClean.Flag.BoolVar(&cleanI, "i", false, "")
	cmdClean.Flag.BoolVar(&cleanN, "n", false, "")
	cmdClean.Flag.BoolVar(&cleanR, "r", false, "")
	cmdClean.Flag.BoolVar(&cleanX, "x", false, "")
}

func runClean(cmd *Command, args []string) {
	for _, pkg := range packagesAndErrors(args) {
		clean(pkg)
	}
}

var cleaned = map[*Package]bool{}

// TODO: These are dregs left by Makefile-based builds.
// Eventually, can stop deleting these.
var cleanDir = map[string]bool{
	"_test": true,
	"_obj":  true,
}

var cleanFile = map[string]bool{
	"_testmain.go": true,
	"test.out":     true,
	"build.out":    true,
	"a.out":        true,
}

var cleanExt = map[string]bool{
	".5":  true,
	".6":  true,
	".8":  true,
	".a":  true,
	".o":  true,
	".so": true,
}

func clean(p *Package) {
	if cleaned[p] {
		return
	}
	if p.Dir == "" {
		errorf("can't load package: %v", p.Error)
		return
	}
	dirs, err := ioutil.ReadDir(p.Dir)
	if err != nil {
		errorf("go clean %s: %v", p.Dir, err)
		return
	}

	var b builder
	b.print = fmt.Print

	packageFile := map[string]bool{}
	if p.Name != "main" {
		// Record which files are not in package main.
		// The others are.
		keep := func(list []string) {
			for _, f := range list {
				packageFile[f] = true
			}
		}
		keep(p.GoFiles)
		keep(p.CgoFiles)
		keep(p.TestGoFiles)
		keep(p.XTestGoFiles)
	}

	_, elem := filepath.Split(p.Dir)
	allRemove := []string{
		elem,
		elem + ".exe",
		elem + ".test",
		elem + ".test.exe",
	}
	for _, dir := range dirs {
		name := dir.Name()
		if packageFile[name] {
			continue
		}
		if !dir.IsDir() && strings.HasSuffix(name, ".go") {
			base := name[:len(name)-len(".go")]
			allRemove = append(allRemove, base, base+".exe")
		}
	}
	if cleanN || cleanX {
		b.showcmd(p.Dir, "rm -f %s", strings.Join(allRemove, " "))
	}

	toRemove := map[string]bool{}
	for _, name := range allRemove {
		toRemove[name] = true
	}
	for _, dir := range dirs {
		name := dir.Name()
		if dir.IsDir() {
			// TODO: Remove once Makefiles are forgotten.
			if cleanDir[name] {
				if cleanN || cleanX {
					b.showcmd(p.Dir, "rm -r %s", name)
					if cleanN {
						continue
					}
				}
				if err := os.RemoveAll(filepath.Join(p.Dir, name)); err != nil {
					errorf("go clean: %v", err)
				}
			}
			continue
		}

		if cleanN {
			continue
		}

		if cleanFile[name] || cleanExt[filepath.Ext(name)] || toRemove[name] {
			removeFile(filepath.Join(p.Dir, name))
		}
	}

	if cleanI && p.target != "" {
		if cleanN || cleanX {
			b.showcmd("", "rm -f %s", p.target)
		}
		if !cleanN {
			removeFile(p.target)
		}
	}

	if cleanI && p.usesSwig() {
		for _, f := range stringList(p.SwigFiles, p.SwigCXXFiles) {
			dir := p.swigDir(&buildContext)
			soname := p.swigSoname(f)
			target := filepath.Join(dir, soname)
			if cleanN || cleanX {
				b.showcmd("", "rm -f %s", target)
			}
			if !cleanN {
				removeFile(target)
			}
		}
	}

	if cleanR {
		for _, p1 := range p.imports {
			clean(p1)
		}
	}
}

// removeFile tries to remove file f, if error other than file doesn't exist
// occurs, it will report the error.
func removeFile(f string) {
	if err := os.Remove(f); err != nil && !os.IsNotExist(err) {
		errorf("go clean: %v", err)
	}
}
<|endoftext|>


<commit_before>package gen

import "github.com/leanovate/gopter"

// Complex128Box generate complex128 numbers within a rectangle/box in the complex plane
func Complex128Box(min, max complex128) gopter.Gen {
	return gopter.CombineGens(
		Float64Range(real(min), real(max)),
		Float64Range(imag(min), imag(max)),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float64), values[1].(float64))
	}).SuchThat(func(v interface{}) bool {
		c := v.(complex128)
		return real(c) >= real(min) && real(c) <= real(max) &&
			imag(c) >= imag(min) && imag(c) <= imag(max)
	}).WithShrinker(Complex128Shrinker)
}

// Complex128 generate arbitrary complex128 numbers
func Complex128() gopter.Gen {
	return gopter.CombineGens(
		Float64(),
		Float64(),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float64), values[1].(float64))
	}).WithShrinker(Complex128Shrinker)
}

// Complex64Box generate complex64 numbers within a rectangle/box in the complex plane
func Complex64Box(min, max complex64) gopter.Gen {
	return gopter.CombineGens(
		Float32Range(real(min), real(max)),
		Float32Range(imag(min), imag(max)),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float32), values[1].(float32))
	}).SuchThat(func(v interface{}) bool {
		c := v.(complex64)
		return real(c) >= real(min) && real(c) <= real(max) &&
			imag(c) >= imag(min) && imag(c) <= imag(max)
	}).WithShrinker(Complex64Shrinker)
}

// Complex64 generate arbitrary complex64 numbers
func Complex64() gopter.Gen {
	return gopter.CombineGens(
		Float32(),
		Float32(),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float32), values[1].(float32))
	}).WithShrinker(Complex128Shrinker)
}
<commit_msg>Fix the Complex64 generator to correctly specify Complex64Shrinker (instead of the 128 version).<commit_after>package gen

import "github.com/leanovate/gopter"

// Complex128Box generate complex128 numbers within a rectangle/box in the complex plane
func Complex128Box(min, max complex128) gopter.Gen {
	return gopter.CombineGens(
		Float64Range(real(min), real(max)),
		Float64Range(imag(min), imag(max)),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float64), values[1].(float64))
	}).SuchThat(func(v interface{}) bool {
		c := v.(complex128)
		return real(c) >= real(min) && real(c) <= real(max) &&
			imag(c) >= imag(min) && imag(c) <= imag(max)
	}).WithShrinker(Complex128Shrinker)
}

// Complex128 generate arbitrary complex128 numbers
func Complex128() gopter.Gen {
	return gopter.CombineGens(
		Float64(),
		Float64(),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float64), values[1].(float64))
	}).WithShrinker(Complex128Shrinker)
}

// Complex64Box generate complex64 numbers within a rectangle/box in the complex plane
func Complex64Box(min, max complex64) gopter.Gen {
	return gopter.CombineGens(
		Float32Range(real(min), real(max)),
		Float32Range(imag(min), imag(max)),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float32), values[1].(float32))
	}).SuchThat(func(v interface{}) bool {
		c := v.(complex64)
		return real(c) >= real(min) && real(c) <= real(max) &&
			imag(c) >= imag(min) && imag(c) <= imag(max)
	}).WithShrinker(Complex64Shrinker)
}

// Complex64 generate arbitrary complex64 numbers
func Complex64() gopter.Gen {
	return gopter.CombineGens(
		Float32(),
		Float32(),
	).Map(func(v interface{}) interface{} {
		values := v.([]interface{})
		return complex(values[0].(float32), values[1].(float32))
	}).WithShrinker(Complex64Shrinker)
}
<|endoftext|>


<commit_before>package main

import (
	"strings"

	. "github.com/davelondon/jennifer/jen"
)

func main() {

	file := NewFile("jen")

	file.Comment("\tThis file is generated by genjen - do not edit!\n")
	file.Line()

	for _, b := range Blocks {
		b := b // b used in closures
		if b.Name == "" {
			continue
		}
		comment := Commentf("%s inserts %s", b.Name, b.Desc)

		/*
			func {Name}(c ...Code) *Group {
				return newStatement().{Name}(c...)
			}
		*/
		file.Add(comment)
		file.Func().Id(b.Name).Params(
			Id("c").Op("...").Id("Code"),
		).Op("*").Id("Group").Block(
			Return().Id("newStatement").Call().Op(".").Id(b.Name).Call(
				Id("c").Op("..."),
			),
		)

		/*
			func (g *Group) {Name}(c ...Code) *Group {
				if startNewStatement(g.syntax) {
					s := {Name}(c...)
					g.items = append(g.items, s)
					return s
				}
				s := Group{
					syntax: {Syntax},
					items:  c,
				}
				g.items = append(g.items, s)
				return g
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(b.Name).Params(
			Id("c").Op("...").Id("Code"),
		).Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(
				Id("g", "syntax"),
			).Block(
				Id("s").Op(":=").Id(b.Name).Call(
					Id("c").Op("..."),
				),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Id("s").Op(":=").Op("&").Id("Group").Dict(map[Code]Code{
				Id("syntax"): Id(b.Syntax),
				Id("items"):  Id("c"),
			}),
			Id("g", "items").Op("=").Append(
				Id("g", "items"),
				Id("s"),
			),
			Return(Id("g")),
		)

		nameFunc := b.Name + "Func"
		/*
			func {NameFunc}(f func(*Group)) *Group {
				return newStatement().{NameFunc}(c...)
			}
		*/
		file.Add(comment)
		file.Func().Id(nameFunc).Params(
			Id("f").Func().Params(Op("*").Id("Group")),
		).Op("*").Id("Group").Block(
			Return().Id("newStatement").Call().Op(".").Id(nameFunc).Call(
				Id("f"),
			),
		)

		/*
			func (g *Group) {NameFunc}(f func(*Group)) *Group {
				if startNewStatement(g.syntax) {
					s := {NameFunc}(f)
					g.items = append(g.items, s)
					return s
				}
				s := &Group{
					syntax:    {Syntax},
				}
				f(s)
				g.items = append(g.items, s)
				return g
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(nameFunc).Params(
			Id("f").Func().Params(Op("*").Id("Group")),
		).Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(
				Id("g", "syntax"),
			).Block(
				Id("s").Op(":=").Id(nameFunc).Call(
					Id("f"),
				),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Id("s").Op(":=").Op("&").Id("Group").Dict(map[Code]Code{
				Id("syntax"): Id(b.Syntax),
			}),
			Id("f").Call(Id("s")),
			Id("g", "items").Op("=").Append(
				Id("g", "items"),
				Id("s"),
			),
			Return(Id("g")),
		)
	}

	type tkn struct {
		name      string
		cap       string
		tokenType string
		tokenDesc string
	}
	tokens := []tkn{}
	for _, v := range Identifiers {
		tokens = append(tokens, tkn{
			name:      v,
			cap:       strings.ToUpper(v[:1]) + v[1:],
			tokenType: "identifierToken",
			tokenDesc: "identifier",
		})
	}
	for _, v := range Keywords {
		tokens = append(tokens, tkn{
			name:      v,
			cap:       strings.ToUpper(v[:1]) + v[1:],
			tokenType: "keywordToken",
			tokenDesc: "keyword",
		})
	}

	for _, t := range tokens {
		t := t // used in closures
		comment := Commentf(
			"%s inserts the %s %s",
			t.cap,
			t.name,
			t.tokenDesc,
		)
		/*
			func {Name}() *Group {
				return newStatement().{Name}()
			}
		*/
		file.Add(comment)
		file.Func().Id(t.cap).Params().Op("*").Id("Group").Block(
			Return().Id("newStatement").Call().Op(".").Id(t.cap).Call(),
		)

		/*
			func (g *Group) {Name}() *Group {
				if startNewStatement(g.syntax) {
					s := {Name}()
					g.items = append(g.items, s)
					return s
				}
				t := token{
					Group:    g,
					typ:     {identifierToken|keywordToken},
					content: "{Name}",
				}
				g.items = append(g.items, t)
				return g
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(t.cap).Params().Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(
				Id("g", "syntax"),
			).Block(
				Id("s").Op(":=").Id(t.cap).Call(),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Id("t").Op(":=").Id("token").Dict(map[Code]Code{
				Id("Group"):   Id("g"),
				Id("typ"):     Id(t.tokenType),
				Id("content"): Lit(t.name),
			}),
			Id("g", "items").Op("=").Append(
				Id("g", "items"),
				Id("t"),
			),
			Return(Id("g")),
		)
	}

	for _, f := range Functions {
		f := f // used in closure
		capName := strings.ToUpper(f[:1]) + f[1:]
		comment := Commentf(
			"%s inserts the built in function %s",
			capName,
			f,
		)
		/*
			func {Name}(c ...Code) *Group {
				return newStatement().{Name}(c...)
			}
		*/
		file.Add(comment)
		file.Func().Id(capName).Params(
			Id("c").Op("...").Id("Code"),
		).Op("*").Id("Group").Block(
			Return().Id("newStatement").Call().Op(".").Id(capName).Call(
				Id("c").Op("..."),
			),
		)

		/*
			func (g *Group) {Name}(c ...Code) *Group {
				if startNewStatement(g.syntax) {
					s := {Name}(c...)
					g.items = append(g.items, s)
					return s
				}
				return g.Id("{Name}").{Call|List}(c...)
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(capName).Params(
			Id("c").Op("...").Id("Code"),
		).Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(Id("g", "syntax")).Block(
				Id("s").Op(":=").Id(capName).Call(
					Id("c").Op("..."),
				),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Return().Id("g", "Id").Call(
				Lit(f),
			).Op(".").Id("Call").Call(
				Id("c").Op("..."),
			),
		)
	}

	err := file.Save("./generated.go")
	//err := file.Render(os.Stdout)
	if err != nil {
		panic(err)
	}

}
<commit_msg>Extracted common redirect functions<commit_after>package main

import (
	"strings"

	. "github.com/davelondon/jennifer/jen"
)

func main() {

	file := NewFile("jen")

	file.Comment("\tThis file is generated by genjen - do not edit!\n")
	file.Line()

	// This generates a function for each method. The
	// funciton creates a new statement and calls the
	// method.
	redirect := func(
		name string,
		comment Code,
		params []Code,
		call []Code,
	) {
		/*
			// {comment}
			func {name}({params}) *Group {
				return newStatement().{name}({call})
			}
		*/
		file.Add(comment)
		file.Func().Id(name).Params(
			params...,
		).Op("*").Id("Group").Block(
			Return().Id("newStatement").Call().Op(".").Id(name).Call(
				call...,
			),
		)
	}

	for _, b := range Blocks {
		b := b // b used in closures
		if b.Name == "" {
			continue
		}
		comment := Commentf("%s inserts %s", b.Name, b.Desc)

		redirect(
			b.Name,
			comment,
			[]Code{Id("c").Op("...").Id("Code")},
			[]Code{Id("c").Op("...")},
		)

		/*
			func (g *Group) {Name}(c ...Code) *Group {
				if startNewStatement(g.syntax) {
					s := {Name}(c...)
					g.items = append(g.items, s)
					return s
				}
				s := Group{
					syntax: {Syntax},
					items:  c,
				}
				g.items = append(g.items, s)
				return g
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(b.Name).Params(
			Id("c").Op("...").Id("Code"),
		).Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(
				Id("g", "syntax"),
			).Block(
				Id("s").Op(":=").Id(b.Name).Call(
					Id("c").Op("..."),
				),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Id("s").Op(":=").Op("&").Id("Group").Dict(map[Code]Code{
				Id("syntax"): Id(b.Syntax),
				Id("items"):  Id("c"),
			}),
			Id("g", "items").Op("=").Append(
				Id("g", "items"),
				Id("s"),
			),
			Return(Id("g")),
		)

		nameFunc := b.Name + "Func"
		redirect(
			nameFunc,
			comment,
			[]Code{Id("f").Func().Params(Op("*").Id("Group"))},
			[]Code{Id("f")},
		)

		/*
			func (g *Group) {NameFunc}(f func(*Group)) *Group {
				if startNewStatement(g.syntax) {
					s := {NameFunc}(f)
					g.items = append(g.items, s)
					return s
				}
				s := &Group{
					syntax:    {Syntax},
				}
				f(s)
				g.items = append(g.items, s)
				return g
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(nameFunc).Params(
			Id("f").Func().Params(Op("*").Id("Group")),
		).Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(
				Id("g", "syntax"),
			).Block(
				Id("s").Op(":=").Id(nameFunc).Call(
					Id("f"),
				),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Id("s").Op(":=").Op("&").Id("Group").Dict(map[Code]Code{
				Id("syntax"): Id(b.Syntax),
			}),
			Id("f").Call(Id("s")),
			Id("g", "items").Op("=").Append(
				Id("g", "items"),
				Id("s"),
			),
			Return(Id("g")),
		)
	}

	type tkn struct {
		name      string
		cap       string
		tokenType string
		tokenDesc string
	}
	tokens := []tkn{}
	for _, v := range Identifiers {
		tokens = append(tokens, tkn{
			name:      v,
			cap:       strings.ToUpper(v[:1]) + v[1:],
			tokenType: "identifierToken",
			tokenDesc: "identifier",
		})
	}
	for _, v := range Keywords {
		tokens = append(tokens, tkn{
			name:      v,
			cap:       strings.ToUpper(v[:1]) + v[1:],
			tokenType: "keywordToken",
			tokenDesc: "keyword",
		})
	}

	for _, t := range tokens {
		t := t // used in closures
		comment := Commentf(
			"%s inserts the %s %s",
			t.cap,
			t.name,
			t.tokenDesc,
		)
		redirect(
			t.cap,
			comment,
			[]Code{},
			[]Code{},
		)

		/*
			func (g *Group) {Name}() *Group {
				if startNewStatement(g.syntax) {
					s := {Name}()
					g.items = append(g.items, s)
					return s
				}
				t := token{
					Group:    g,
					typ:     {identifierToken|keywordToken},
					content: "{Name}",
				}
				g.items = append(g.items, t)
				return g
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(t.cap).Params().Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(
				Id("g", "syntax"),
			).Block(
				Id("s").Op(":=").Id(t.cap).Call(),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Id("t").Op(":=").Id("token").Dict(map[Code]Code{
				Id("Group"):   Id("g"),
				Id("typ"):     Id(t.tokenType),
				Id("content"): Lit(t.name),
			}),
			Id("g", "items").Op("=").Append(
				Id("g", "items"),
				Id("t"),
			),
			Return(Id("g")),
		)
	}

	for _, f := range Functions {
		f := f // used in closure
		capName := strings.ToUpper(f[:1]) + f[1:]
		comment := Commentf(
			"%s inserts the built in function %s",
			capName,
			f,
		)

		redirect(
			capName,
			comment,
			[]Code{Id("c").Op("...").Id("Code")},
			[]Code{Id("c").Op("...")},
		)

		/*
			func (g *Group) {Name}(c ...Code) *Group {
				if startNewStatement(g.syntax) {
					s := {Name}(c...)
					g.items = append(g.items, s)
					return s
				}
				return g.Id("{Name}").{Call|List}(c...)
			}
		*/
		file.Add(comment)
		file.Func().Params(
			Id("g").Op("*").Id("Group"),
		).Id(capName).Params(
			Id("c").Op("...").Id("Code"),
		).Op("*").Id("Group").Block(
			If().Id("startNewStatement").Call(Id("g", "syntax")).Block(
				Id("s").Op(":=").Id(capName).Call(
					Id("c").Op("..."),
				),
				Id("g", "items").Op("=").Append(
					Id("g", "items"),
					Id("s"),
				),
				Return(Id("s")),
			),
			Return().Id("g", "Id").Call(
				Lit(f),
			).Op(".").Id("Call").Call(
				Id("c").Op("..."),
			),
		)
	}

	err := file.Save("./generated.go")
	//err := file.Render(os.Stdout)
	if err != nil {
		panic(err)
	}

}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2014-2015, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package qan

import (
	"fmt"
	"log"
	"runtime/debug"
	"sync"
	"time"

	"github.com/percona/percona-agent/data"
	"github.com/percona/percona-agent/mysql"
	"github.com/percona/percona-agent/pct"
	"github.com/percona/percona-agent/ticker"
)

// A Worker gets queries, aggregates them, and returns a Result. Workers are ran
// by Analyzers. When ran, MySQL is presumed to be configured and ready.
type Worker interface {
	Setup(*Interval) error
	Run() (*Result, error)
	Stop() error
	Cleanup() error
	Status() map[string]string
}

// An Analyzer runs a Worker at each Interval. Analyzers are responsible for
// MySQL: configuring, restarts, etc. The Worker is only ran when MySQL is
// configured and ready. Analyzers are also responsible for making Reports from
// the Results returned by Workers. The Worker determines the type of Analyzer:
// slowlog or perfschema. Analyzers are ran by the QAN Manager.
type Analyzer interface {
	Start() error
	Stop() error
	Status() map[string]string
	String() string
}

// An AnalyzerFactory makes an Analyzer, real or mock.
type AnalyzerFactory interface {
	Make(config Config, name string, mysqlConn mysql.Connector, restartChan <-chan bool, tickChan chan time.Time) Analyzer
}

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

type RealAnalyzer struct {
	logger      *pct.Logger
	config      Config
	iter        IntervalIter
	mysqlConn   mysql.Connector
	restartChan <-chan bool
	worker      Worker
	clock       ticker.Manager
	spool       data.Spooler
	// --
	name                string
	mysqlConfiguredChan chan bool
	workerDoneChan      chan *Interval
	status              *pct.Status
	runSync             *pct.SyncChan
	configureMySQLSync  *pct.SyncChan
	running             bool
	mux                 *sync.RWMutex
}

func NewRealAnalyzer(logger *pct.Logger, config Config, iter IntervalIter, mysqlConn mysql.Connector, restartChan <-chan bool, worker Worker, clock ticker.Manager, spool data.Spooler) *RealAnalyzer {
	name := logger.Service()
	a := &RealAnalyzer{
		logger:      logger,
		config:      config,
		iter:        iter,
		mysqlConn:   mysqlConn,
		restartChan: restartChan,
		worker:      worker,
		clock:       clock,
		spool:       spool,
		// --
		name:                name,
		mysqlConfiguredChan: make(chan bool, 1),
		workerDoneChan:      make(chan *Interval, 1),
		status:              pct.NewStatus([]string{name, name + "-last-interval", name + "-next-interval"}),
		runSync:             pct.NewSyncChan(),
		configureMySQLSync:  pct.NewSyncChan(),
		mux:                 &sync.RWMutex{},
	}
	return a
}

func (a *RealAnalyzer) String() string {
	return a.name
}

func (a *RealAnalyzer) Start() error {
	a.logger.Debug("Start:call")
	defer a.logger.Debug("Start:return")
	a.mux.Lock()
	defer a.mux.Unlock()
	if a.running {
		return nil
	}
	go a.run()
	a.running = true
	return nil
}

func (a *RealAnalyzer) Stop() error {
	a.logger.Debug("Stop:call")
	defer a.logger.Debug("Stop:return")
	a.mux.Lock()
	defer a.mux.Unlock()
	if !a.running {
		return nil
	}
	a.runSync.Stop()
	a.runSync.Wait()
	a.running = false
	return nil
}

func (a *RealAnalyzer) Status() map[string]string {
	a.mux.RLock()
	defer a.mux.RUnlock()
	if a.running {
		a.status.Update(a.name+"-next-interval", fmt.Sprintf("%.1fs", a.clock.ETA(a.iter.TickChan())))
	} else {
		a.status.Update(a.name+"-next-interval", "")
	}
	return a.status.Merge(a.worker.Status())
}

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

func (a *RealAnalyzer) configureMySQL(config []mysql.Query, tryLimit int) {
	a.logger.Debug("configureMySQL:call")
	defer func() {
		if err := recover(); err != nil {
			a.logger.Error(a.name+":configureMySQL crashed: ", err)
		}
		a.logger.Debug("configureMySQL:return")
	}()

	try := 0
	for (tryLimit == 0) || (try < tryLimit) {
		try++

		select {
		case <-a.configureMySQLSync.StopChan:
			a.logger.Debug("configureMySQL:stop")
			a.configureMySQLSync.Done()
			return
		default:
		}

		// Connect handles backoff internally.
		a.logger.Debug("configureMySQL:connecting")
		if err := a.mysqlConn.Connect(1); err != nil {
			a.logger.Warn("Cannot connect to MySQL:", err)
			continue
		}
		defer a.mysqlConn.Close()

		a.logger.Debug("configureMySQL:configuring")
		if err := a.mysqlConn.Set(config); err != nil {
			a.mysqlConn.Close()
			a.logger.Warn("Cannot configure MySQL:", err)
			continue
		}

		a.logger.Debug("configureMySQL:configured")
		select {
		case a.mysqlConfiguredChan <- true:
			return // success
		case <-a.configureMySQLSync.StopChan:
			a.logger.Debug("configureMySQL:stop")
			a.configureMySQLSync.Done()
			return
		}
	}
}

func (a *RealAnalyzer) run() {
	a.logger.Debug("run:call")
	defer a.logger.Debug("run:return")

	mysqlConfigured := false
	go a.configureMySQL(a.config.Start, 0) // try forever

	defer func() {
		a.status.Update(a.name, "Stopping worker")
		a.logger.Info("Stopping worker")
		a.worker.Stop()

		a.status.Update(a.name, "Stopping interval iter")
		a.logger.Info("Stopping interval iter")
		a.iter.Stop()

		if !mysqlConfigured {
			a.status.Update(a.name, "Stopping MySQL config")
			a.logger.Info("Stopping MySQL config")
			a.configureMySQLSync.Stop()
			a.configureMySQLSync.Wait()
		}

		a.status.Update(a.name, "Stopping QAN on MySQL")
		a.logger.Info("Stopping QAN on MySQL")
		a.configureMySQL(a.config.Stop, 1) // try once

		if err := recover(); err != nil {
			a.logger.Error(a.name+" crashed: ", err)
			a.status.Update(a.name, "Crashed")
		} else {
			a.status.Update(a.name, "Stopped")
		}

		a.runSync.Done()
	}()

	workerRunning := false
	lastTs := time.Time{}
	currentInterval := &Interval{}
	for {
		a.logger.Debug("run:idle")
		if mysqlConfigured {
			if workerRunning {
				a.status.Update(a.name, "Running")
			} else {
				a.status.Update(a.name, "Idle")
			}
		} else {
			a.status.Update(a.name, "Trying to configure MySQL")
		}

		select {
		case interval := <-a.iter.IntervalChan():
			if !mysqlConfigured {
				a.logger.Debug(fmt.Sprintf("run:interval:%d:skip (mysql not configured)", interval.Number))
				continue
			}

			if workerRunning {
				a.logger.Warn(fmt.Sprintf("Skipping interval '%s' because interval '%s' is still being parsed",
					interval, currentInterval))
				continue
			}

			a.status.Update(a.name, fmt.Sprintf("Starting interval '%s'", interval))
			a.logger.Debug(fmt.Sprintf("run:interval:%s", interval))
			currentInterval = interval

			// Run the worker, timing it, make a report from its results, spool
			// the report. When done the interval is returned on workerDoneChan.
			go a.runWorker(interval)
			workerRunning = true
		case interval := <-a.workerDoneChan:
			a.logger.Debug("run:worker:done")
			a.status.Update(a.name, fmt.Sprintf("Cleaning up after interval '%s'", interval))
			workerRunning = false

			if interval.StartTime.After(lastTs) {
				t0 := interval.StartTime.Format("2006-01-02 15:04:05")
				if a.config.CollectFrom == "slowlog" {
					t1 := interval.StopTime.Format("15:04:05 MST")
					a.status.Update(a.name+"-last-interval", fmt.Sprintf("%s to %s", t0, t1))
				} else {
					a.status.Update(a.name+"-last-interval", fmt.Sprintf("%s", t0))
				}
				lastTs = interval.StartTime
			}
		case mysqlConfigured = <-a.mysqlConfiguredChan:
			a.logger.Debug("run:mysql:configured")
			// Start the IntervalIter once MySQL has been configured.
			// This avoids no data or partial data, e.g. slow log verbosity
			// not set yet.
			a.iter.Start()

			// If the next interval is more than 1 minute in the future,
			// simulate a clock tick now to start the iter early. For example,
			// if the interval is 5m and it's currently 01:00, the next interval
			// starts in 4m and stops in 9m, so data won't be reported for about
			// 10m. Instead, tick now so start interval=01:00 and end interval
			// =05:00 and data is reported in about 6m.
			tickChan := a.iter.TickChan()
			t := a.clock.ETA(tickChan)
			if t > 60 {
				began := ticker.Began(a.config.Interval, uint(time.Now().UTC().Unix()))
				a.logger.Info("First interval began at", began)
				tickChan <- began
			} else {
				a.logger.Info(fmt.Sprintf("First interval begins in %.1f seconds", t))
			}
		case <-a.restartChan:
			a.logger.Debug("run:mysql:restart")
			// If MySQL is not configured, then configureMySQL() should already
			// be running, trying to configure it. Else, we need to run
			// configureMySQL again.
			if mysqlConfigured {
				mysqlConfigured = false
				a.iter.Stop()
				go a.configureMySQL(a.config.Start, 0) // try forever
			}
		case <-a.runSync.StopChan:
			a.logger.Debug("run:stop")
			return
		}
	}
}

func (a *RealAnalyzer) runWorker(interval *Interval) {
	a.logger.Debug(fmt.Sprintf("runWorker:call:%d", interval.Number))
	defer func() {
		if err := recover(); err != nil {
			errMsg := fmt.Sprintf(a.name+"-worker crashed: '%s': %s", interval, err)
			log.Println(errMsg)
			debug.PrintStack()
			a.logger.Error(errMsg)
		}
		a.workerDoneChan <- interval
		a.logger.Debug(fmt.Sprintf("runWorker:return:%d", interval.Number))
	}()

	// Let worker do whatever it needs before it starts processing
	// the interval. This mostly makes testing easier.
	if err := a.worker.Setup(interval); err != nil {
		a.logger.Warn(err)
		return
	}

	// Let worker do whatever it needs after processing the interval.
	// This mostly maske testing easier.
	defer func() {
		if err := a.worker.Cleanup(); err != nil {
			a.logger.Warn(err)
		}
	}()

	// Run the worker to process the interval.
	t0 := time.Now()
	result, err := a.worker.Run()
	t1 := time.Now()
	if err != nil {
		a.logger.Error(err)
		return
	}
	if result == nil {
		if a.config.CollectFrom == "slowlog" {
			// This shouldn't happen. If it does, the slow log worker has a bug
			// because it should have returned an error above.
			a.logger.Error("Nil result", interval)
		}
		return
	}
	result.RunTime = t1.Sub(t0).Seconds()

	// Translate the results into a report and spool.
	// NOTE: "qan" here is correct; do not use a.name.
	report := MakeReport(a.config, interval, result)
	if err := a.spool.Write("qan", report); err != nil {
		a.logger.Warn("Lost report:", err)
	}
}
<commit_msg>Don't log every step when stopping QAN.<commit_after>/*
   Copyright (c) 2014-2015, Percona LLC and/or its affiliates. All rights reserved.

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package qan

import (
	"fmt"
	"log"
	"runtime/debug"
	"sync"
	"time"

	"github.com/percona/percona-agent/data"
	"github.com/percona/percona-agent/mysql"
	"github.com/percona/percona-agent/pct"
	"github.com/percona/percona-agent/ticker"
)

// A Worker gets queries, aggregates them, and returns a Result. Workers are ran
// by Analyzers. When ran, MySQL is presumed to be configured and ready.
type Worker interface {
	Setup(*Interval) error
	Run() (*Result, error)
	Stop() error
	Cleanup() error
	Status() map[string]string
}

// An Analyzer runs a Worker at each Interval. Analyzers are responsible for
// MySQL: configuring, restarts, etc. The Worker is only ran when MySQL is
// configured and ready. Analyzers are also responsible for making Reports from
// the Results returned by Workers. The Worker determines the type of Analyzer:
// slowlog or perfschema. Analyzers are ran by the QAN Manager.
type Analyzer interface {
	Start() error
	Stop() error
	Status() map[string]string
	String() string
}

// An AnalyzerFactory makes an Analyzer, real or mock.
type AnalyzerFactory interface {
	Make(config Config, name string, mysqlConn mysql.Connector, restartChan <-chan bool, tickChan chan time.Time) Analyzer
}

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

type RealAnalyzer struct {
	logger      *pct.Logger
	config      Config
	iter        IntervalIter
	mysqlConn   mysql.Connector
	restartChan <-chan bool
	worker      Worker
	clock       ticker.Manager
	spool       data.Spooler
	// --
	name                string
	mysqlConfiguredChan chan bool
	workerDoneChan      chan *Interval
	status              *pct.Status
	runSync             *pct.SyncChan
	configureMySQLSync  *pct.SyncChan
	running             bool
	mux                 *sync.RWMutex
}

func NewRealAnalyzer(logger *pct.Logger, config Config, iter IntervalIter, mysqlConn mysql.Connector, restartChan <-chan bool, worker Worker, clock ticker.Manager, spool data.Spooler) *RealAnalyzer {
	name := logger.Service()
	a := &RealAnalyzer{
		logger:      logger,
		config:      config,
		iter:        iter,
		mysqlConn:   mysqlConn,
		restartChan: restartChan,
		worker:      worker,
		clock:       clock,
		spool:       spool,
		// --
		name:                name,
		mysqlConfiguredChan: make(chan bool, 1),
		workerDoneChan:      make(chan *Interval, 1),
		status:              pct.NewStatus([]string{name, name + "-last-interval", name + "-next-interval"}),
		runSync:             pct.NewSyncChan(),
		configureMySQLSync:  pct.NewSyncChan(),
		mux:                 &sync.RWMutex{},
	}
	return a
}

func (a *RealAnalyzer) String() string {
	return a.name
}

func (a *RealAnalyzer) Start() error {
	a.logger.Debug("Start:call")
	defer a.logger.Debug("Start:return")
	a.mux.Lock()
	defer a.mux.Unlock()
	if a.running {
		return nil
	}
	go a.run()
	a.running = true
	return nil
}

func (a *RealAnalyzer) Stop() error {
	a.logger.Debug("Stop:call")
	defer a.logger.Debug("Stop:return")
	a.mux.Lock()
	defer a.mux.Unlock()
	if !a.running {
		return nil
	}
	a.runSync.Stop()
	a.runSync.Wait()
	a.running = false
	return nil
}

func (a *RealAnalyzer) Status() map[string]string {
	a.mux.RLock()
	defer a.mux.RUnlock()
	if a.running {
		a.status.Update(a.name+"-next-interval", fmt.Sprintf("%.1fs", a.clock.ETA(a.iter.TickChan())))
	} else {
		a.status.Update(a.name+"-next-interval", "")
	}
	return a.status.Merge(a.worker.Status())
}

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

func (a *RealAnalyzer) configureMySQL(config []mysql.Query, tryLimit int) {
	a.logger.Debug("configureMySQL:call")
	defer func() {
		if err := recover(); err != nil {
			a.logger.Error(a.name+":configureMySQL crashed: ", err)
		}
		a.logger.Debug("configureMySQL:return")
	}()

	try := 0
	for (tryLimit == 0) || (try < tryLimit) {
		try++

		select {
		case <-a.configureMySQLSync.StopChan:
			a.logger.Debug("configureMySQL:stop")
			a.configureMySQLSync.Done()
			return
		default:
		}

		// Connect handles backoff internally.
		a.logger.Debug("configureMySQL:connecting")
		if err := a.mysqlConn.Connect(1); err != nil {
			a.logger.Warn("Cannot connect to MySQL:", err)
			continue
		}
		defer a.mysqlConn.Close()

		a.logger.Debug("configureMySQL:configuring")
		if err := a.mysqlConn.Set(config); err != nil {
			a.mysqlConn.Close()
			a.logger.Warn("Cannot configure MySQL:", err)
			continue
		}

		a.logger.Debug("configureMySQL:configured")
		select {
		case a.mysqlConfiguredChan <- true:
			return // success
		case <-a.configureMySQLSync.StopChan:
			a.logger.Debug("configureMySQL:stop")
			a.configureMySQLSync.Done()
			return
		}
	}
}

func (a *RealAnalyzer) run() {
	a.logger.Debug("run:call")
	defer a.logger.Debug("run:return")

	mysqlConfigured := false
	go a.configureMySQL(a.config.Start, 0) // try forever

	defer func() {
		a.logger.Info("Stopping")

		a.status.Update(a.name, "Stopping worker")
		a.worker.Stop()

		a.status.Update(a.name, "Stopping interval iter")
		a.iter.Stop()

		if !mysqlConfigured {
			a.status.Update(a.name, "Stopping MySQL config")
			a.configureMySQLSync.Stop()
			a.configureMySQLSync.Wait()
		}

		a.status.Update(a.name, "Stopping QAN on MySQL")
		a.configureMySQL(a.config.Stop, 1) // try once

		if err := recover(); err != nil {
			a.logger.Error("QAN crashed: ", err)
			a.status.Update(a.name, "Crashed")
		} else {
			a.status.Update(a.name, "Stopped")
			a.logger.Info("Stopped")
		}

		a.runSync.Done()
	}()

	workerRunning := false
	lastTs := time.Time{}
	currentInterval := &Interval{}
	for {
		a.logger.Debug("run:idle")
		if mysqlConfigured {
			if workerRunning {
				a.status.Update(a.name, "Running")
			} else {
				a.status.Update(a.name, "Idle")
			}
		} else {
			a.status.Update(a.name, "Trying to configure MySQL")
		}

		select {
		case interval := <-a.iter.IntervalChan():
			if !mysqlConfigured {
				a.logger.Debug(fmt.Sprintf("run:interval:%d:skip (mysql not configured)", interval.Number))
				continue
			}

			if workerRunning {
				a.logger.Warn(fmt.Sprintf("Skipping interval '%s' because interval '%s' is still being parsed",
					interval, currentInterval))
				continue
			}

			a.status.Update(a.name, fmt.Sprintf("Starting interval '%s'", interval))
			a.logger.Debug(fmt.Sprintf("run:interval:%s", interval))
			currentInterval = interval

			// Run the worker, timing it, make a report from its results, spool
			// the report. When done the interval is returned on workerDoneChan.
			go a.runWorker(interval)
			workerRunning = true
		case interval := <-a.workerDoneChan:
			a.logger.Debug("run:worker:done")
			a.status.Update(a.name, fmt.Sprintf("Cleaning up after interval '%s'", interval))
			workerRunning = false

			if interval.StartTime.After(lastTs) {
				t0 := interval.StartTime.Format("2006-01-02 15:04:05")
				if a.config.CollectFrom == "slowlog" {
					t1 := interval.StopTime.Format("15:04:05 MST")
					a.status.Update(a.name+"-last-interval", fmt.Sprintf("%s to %s", t0, t1))
				} else {
					a.status.Update(a.name+"-last-interval", fmt.Sprintf("%s", t0))
				}
				lastTs = interval.StartTime
			}
		case mysqlConfigured = <-a.mysqlConfiguredChan:
			a.logger.Debug("run:mysql:configured")
			// Start the IntervalIter once MySQL has been configured.
			// This avoids no data or partial data, e.g. slow log verbosity
			// not set yet.
			a.iter.Start()

			// If the next interval is more than 1 minute in the future,
			// simulate a clock tick now to start the iter early. For example,
			// if the interval is 5m and it's currently 01:00, the next interval
			// starts in 4m and stops in 9m, so data won't be reported for about
			// 10m. Instead, tick now so start interval=01:00 and end interval
			// =05:00 and data is reported in about 6m.
			tickChan := a.iter.TickChan()
			t := a.clock.ETA(tickChan)
			if t > 60 {
				began := ticker.Began(a.config.Interval, uint(time.Now().UTC().Unix()))
				a.logger.Info("First interval began at", began)
				tickChan <- began
			} else {
				a.logger.Info(fmt.Sprintf("First interval begins in %.1f seconds", t))
			}
		case <-a.restartChan:
			a.logger.Debug("run:mysql:restart")
			// If MySQL is not configured, then configureMySQL() should already
			// be running, trying to configure it. Else, we need to run
			// configureMySQL again.
			if mysqlConfigured {
				mysqlConfigured = false
				a.iter.Stop()
				go a.configureMySQL(a.config.Start, 0) // try forever
			}
		case <-a.runSync.StopChan:
			a.logger.Debug("run:stop")
			return
		}
	}
}

func (a *RealAnalyzer) runWorker(interval *Interval) {
	a.logger.Debug(fmt.Sprintf("runWorker:call:%d", interval.Number))
	defer func() {
		if err := recover(); err != nil {
			errMsg := fmt.Sprintf(a.name+"-worker crashed: '%s': %s", interval, err)
			log.Println(errMsg)
			debug.PrintStack()
			a.logger.Error(errMsg)
		}
		a.workerDoneChan <- interval
		a.logger.Debug(fmt.Sprintf("runWorker:return:%d", interval.Number))
	}()

	// Let worker do whatever it needs before it starts processing
	// the interval. This mostly makes testing easier.
	if err := a.worker.Setup(interval); err != nil {
		a.logger.Warn(err)
		return
	}

	// Let worker do whatever it needs after processing the interval.
	// This mostly maske testing easier.
	defer func() {
		if err := a.worker.Cleanup(); err != nil {
			a.logger.Warn(err)
		}
	}()

	// Run the worker to process the interval.
	t0 := time.Now()
	result, err := a.worker.Run()
	t1 := time.Now()
	if err != nil {
		a.logger.Error(err)
		return
	}
	if result == nil {
		if a.config.CollectFrom == "slowlog" {
			// This shouldn't happen. If it does, the slow log worker has a bug
			// because it should have returned an error above.
			a.logger.Error("Nil result", interval)
		}
		return
	}
	result.RunTime = t1.Sub(t0).Seconds()

	// Translate the results into a report and spool.
	// NOTE: "qan" here is correct; do not use a.name.
	report := MakeReport(a.config, interval, result)
	if err := a.spool.Write("qan", report); err != nil {
		a.logger.Warn("Lost report:", err)
	}
}
<|endoftext|>


<commit_before>package engine

import "testing"

func TestInvoke(t *testing.T) {
    err := Invoke(Opts())
    if err != nil {
        t.Errorf("Unexpected Exec error %s", err)
    }
}
<commit_msg>Provide command opts when testing package<commit_after>package engine

import "testing"

func TestInvoke(t *testing.T) {
    opts := Opts()
    opts.files = []string{"file1", "file2"}
    opts.backend = "HTML5"
    opts.doctype = "book"
    opts.outFile = "targetFile"
    opts.noHeaderFooter = true
    opts.sectionNumbers = true
    opts.attributes = make(map[string]string)
    opts.attributes["att1"] = "value1"
    opts.attributes["att2"] = "value2"
    opts.baseDir = "baseDir"
    opts.destinationDir = "destinationDir"
    err := Invoke(opts)
    if err != nil {
        t.Errorf("Unexpected Exec error %s", err)
    }
}
<|endoftext|>


<commit_before>package gitter

import (
	"fmt"
	"net/http"
	"testing"
)

func TestNew_setToken(t *testing.T) {
	g := New("abc")
	if g.config.token != "abc" {
		t.Logf("Expected %v, got %v", "abc", g.config.token)
	}
}

func TestNew_setAPIBaseURL(t *testing.T) {
	g := New("abc")
	if g.config.apiBaseURL != apiBaseURL {
		t.Logf("Expected %v, got %v", apiBaseURL, g.config.apiBaseURL)
	}
}

func TestNew_setStreamBaseURL(t *testing.T) {
	g := New("abc")
	if g.config.streamBaseURL != streamBaseURL {
		t.Logf("Expected %v, got %v", streamBaseURL, g.config.streamBaseURL)
	}
}

func TestGitter_SetClient(t *testing.T) {
	setup()
	defer teardown()

	c := &http.Client{}
	gitter.SetClient(c)

	if gitter.config.client != c {
		t.Logf("Expected %v, got %v", c, gitter.config.client)
	}
}

func TestGetUser_userData(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            [
              {
                "id": "123",
                "username": "fooBar"
              }
            ]
        `)
	})

	u, err := gitter.GetUser()
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if u.ID != "123" {
		t.Errorf("Expected %v, got %v", "123", u.ID)
	}

	if u.Username != "fooBar" {
		t.Errorf("Expected %v, got %v", "fooBar", u.Username)
	}
}

func TestGetUser_apiEmptyResponseError(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "[]")
	})

	u, err := gitter.GetUser()
	if u != nil {
		t.Errorf("Expected %v, got %v", nil, u)
	}

	if err.Error() != "Failed to retrieve current user" {
		t.Errorf("Expected %v, got %v", "Failed to retrieve current user", err)
	}
}

func TestGetUser_apiError(t *testing.T) {
	setup()
	defer teardown()

	wanted := "json: cannot unmarshal object into Go value of type []gitter.User"

	mux.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            {
              "error": "Unauthorized"
            }
        `)
	})

	u, err := gitter.GetUser()
	if u != nil {
		t.Errorf("Expected %v, got %v", nil, u)
	}

	if err.Error() != wanted {
		t.Errorf("Expected %v, got %v", wanted, err)
	}
}

func TestGetUserRooms(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/user/abc/rooms/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            [
                {
                    "id": "xyz",
                    "name": "wizardamigosinstitute/chat",
                    "topic": "",
                    "uri": "wizardamigosinstitute/chat",
                    "oneToOne": false,
                    "userCount": 12,
                    "unreadItems": 0,
                    "mentions": 0,
                    "lastAccessTime": "2016-02-16T18:47:50.802Z",
                    "lurk": false,
                    "activity": false,
                    "url": "/wizardamigosinstitute/chat",
                    "githubType": "ORG_CHANNEL",
                    "security": "PUBLIC",
                    "noindex": false,
                    "tags": [],
                    "roomMember": true,
                    "v": 9
                },
                {
                    "id": "cde",
                    "name": "codingamigos/chat",
                    "topic": "",
                    "uri": "codingamigos/chat",
                    "oneToOne": false,
                    "userCount": 88,
                    "unreadItems": 0,
                    "mentions": 0,
                    "lastAccessTime": "2016-02-21T15:19:44.733Z",
                    "lurk": false,
                    "activity": false,
                    "url": "/codingamigos/chat",
                    "githubType": "ORG_CHANNEL",
                    "security": "PUBLIC",
                    "noindex": false,
                    "tags": [],
                    "roomMember": true,
                    "v": 55
                }
            ]

            `)
	})

	r, err := gitter.GetUserRooms("abc")
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if len(r) != 2 {
		t.Errorf("Expected %v, got %v", 2, len(r))
	}

	if r[0].ID != "xyz" {
		t.Errorf("Expected %v, got %v", "xyz", r[0].ID)
	}

	if r[1].ID != "cde" {
		t.Errorf("Expected %v, got %v", "cde", r[1].ID)
	}
}

func TestGetRooms(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/rooms/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            [
                {
                    "id": "xyz",
                    "name": "wizardamigosinstitute/chat",
                    "topic": "",
                    "uri": "wizardamigosinstitute/chat",
                    "oneToOne": false,
                    "userCount": 12,
                    "unreadItems": 0,
                    "mentions": 0,
                    "lastAccessTime": "2016-02-16T18:47:50.802Z",
                    "lurk": false,
                    "activity": false,
                    "url": "/wizardamigosinstitute/chat",
                    "githubType": "ORG_CHANNEL",
                    "security": "PUBLIC",
                    "noindex": false,
                    "tags": [],
                    "roomMember": true,
                    "v": 9
                }
            ]

            `)
	})

	r, err := gitter.GetRooms()
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if len(r) != 1 {
		t.Errorf("Expected %v, got %v", 2, len(r))
	}

	if r[0].ID != "xyz" {
		t.Errorf("Expected %v, got %v", "xyz", r[0].ID)
	}
}

func TestGetRoom(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/rooms/xyz/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            {
                "id": "xyz",
                "name": "wizardamigosinstitute/chat",
                "topic": "",
                "uri": "wizardamigosinstitute/chat",
                "oneToOne": false,
                "userCount": 12,
                "unreadItems": 0,
                "mentions": 0,
                "lastAccessTime": "2016-02-16T18:47:50.802Z",
                "lurk": false,
                "activity": false,
                "url": "/wizardamigosinstitute/chat",
                "githubType": "ORG_CHANNEL",
                "security": "PUBLIC",
                "noindex": false,
                "tags": [],
                "roomMember": true,
                "v": 9
            }
            `)
	})

	r, err := gitter.GetRoom("xyz")
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if r.ID != "xyz" {
		t.Errorf("Expected %v, got %v", "xyz", r.ID)
	}
}
<commit_msg>Use only IDs for mock responses<commit_after>package gitter

import (
	"fmt"
	"net/http"
	"testing"
)

func TestNew_setToken(t *testing.T) {
	g := New("abc")
	if g.config.token != "abc" {
		t.Logf("Expected %v, got %v", "abc", g.config.token)
	}
}

func TestNew_setAPIBaseURL(t *testing.T) {
	g := New("abc")
	if g.config.apiBaseURL != apiBaseURL {
		t.Logf("Expected %v, got %v", apiBaseURL, g.config.apiBaseURL)
	}
}

func TestNew_setStreamBaseURL(t *testing.T) {
	g := New("abc")
	if g.config.streamBaseURL != streamBaseURL {
		t.Logf("Expected %v, got %v", streamBaseURL, g.config.streamBaseURL)
	}
}

func TestGitter_SetClient(t *testing.T) {
	setup()
	defer teardown()

	c := &http.Client{}
	gitter.SetClient(c)

	if gitter.config.client != c {
		t.Logf("Expected %v, got %v", c, gitter.config.client)
	}
}

func TestGetUser_userData(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            [
              {
                "id": "123",
                "username": "fooBar"
              }
            ]
        `)
	})

	u, err := gitter.GetUser()
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if u.ID != "123" {
		t.Errorf("Expected %v, got %v", "123", u.ID)
	}

	if u.Username != "fooBar" {
		t.Errorf("Expected %v, got %v", "fooBar", u.Username)
	}
}

func TestGetUser_apiEmptyResponseError(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "[]")
	})

	u, err := gitter.GetUser()
	if u != nil {
		t.Errorf("Expected %v, got %v", nil, u)
	}

	if err.Error() != "Failed to retrieve current user" {
		t.Errorf("Expected %v, got %v", "Failed to retrieve current user", err)
	}
}

func TestGetUser_apiError(t *testing.T) {
	setup()
	defer teardown()

	wanted := "json: cannot unmarshal object into Go value of type []gitter.User"

	mux.HandleFunc("/user/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            {
              "error": "Unauthorized"
            }
        `)
	})

	u, err := gitter.GetUser()
	if u != nil {
		t.Errorf("Expected %v, got %v", nil, u)
	}

	if err.Error() != wanted {
		t.Errorf("Expected %v, got %v", wanted, err)
	}
}

func TestGetUserRooms(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/user/abc/rooms/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            [
                {
                    "id": "xyz"
                },
                {
                    "id": "cde"
                }
            ]

            `)
	})

	r, err := gitter.GetUserRooms("abc")
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if len(r) != 2 {
		t.Errorf("Expected %v, got %v", 2, len(r))
	}

	if r[0].ID != "xyz" {
		t.Errorf("Expected %v, got %v", "xyz", r[0].ID)
	}

	if r[1].ID != "cde" {
		t.Errorf("Expected %v, got %v", "cde", r[1].ID)
	}
}

func TestGetRooms(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/rooms/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            [
                {
                    "id": "xyz"
                }
            ]

            `)
	})

	r, err := gitter.GetRooms()
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if len(r) != 1 {
		t.Errorf("Expected %v, got %v", 2, len(r))
	}

	if r[0].ID != "xyz" {
		t.Errorf("Expected %v, got %v", "xyz", r[0].ID)
	}
}

func TestGetRoom(t *testing.T) {
	setup()
	defer teardown()

	mux.HandleFunc("/rooms/xyz/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, `
            {
                "id": "xyz"
            }
            `)
	})

	r, err := gitter.GetRoom("xyz")
	if err != nil {
		t.Errorf("Expected %v, got %v", nil, err)
	}

	if r.ID != "xyz" {
		t.Errorf("Expected %v, got %v", "xyz", r.ID)
	}
}
<|endoftext|>


<commit_before>// Package glumelogger provides support for Flume NG in Go
//
// It communicates to a Flume NG agent in Thrift via TFramedTranport with TCompactProtocol
// thrift bindings are taken from Flume 1.7[1] (however they should be compatible with 1.6, 1.5, 1.4)
//
// [1] https://raw.githubusercontent.com/apache/flume/flume-1.7/flume-ng-sdk/src/main/thrift/flume.thrift
package glumelogger

import (
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"sync"

	"git.apache.org/thrift.git/lib/go/thrift"
	"github.com/ceocoder/glumelogger/flume"
)

// GlumeLogger holds thrift client and headers
// use NewGlumeLogger to create new one
type GlumeLogger struct {
	client  *flume.ThriftSourceProtocolClient
	headers *map[string]string
	lm      *sync.Mutex
	log     *log.Logger
}

// NewGlumeLogger create a new GlumeLogger client, it requires a host, port and
// map of headers
func NewGlumeLogger(host string, port int, headers *map[string]string) *GlumeLogger {
	var trans thrift.TTransport
	trans, err := thrift.NewTSocket(net.JoinHostPort(host, strconv.Itoa(port)))
	if err != nil {
		fmt.Fprintln(os.Stderr, "error resolving address:", err)
		os.Exit(1)
	}
	trans = thrift.NewTFramedTransport(trans)
	client := flume.NewThriftSourceProtocolClientFactory(trans, thrift.NewTCompactProtocolFactory())
	return &GlumeLogger{client, headers, &sync.Mutex{}, log.New(os.Stdout, "[GlumeLogger] ", log.Ldate|log.Ltime)}
}

// Log forwards message to be logged as array of bytes
// returns status of write and error
//
// append operation is wrapped in a mutex making it thread-safe.
func (l *GlumeLogger) Log(body []byte) (flume.Status, error) {

	event := &flume.ThriftFlumeEvent{*l.headers, body}

	l.lm.Lock()
	defer l.lm.Unlock()
	if !l.client.Transport.IsOpen() {
		l.client.Transport.Open()
	}
	status, err := l.client.Append(event)
	if err != nil {
		// close bad transport proactively for the next write
		log.Printf("Error appending event: %v", err)
		l.client.Transport.Close()
		return status, err
	}

	return status, nil
}
<commit_msg>transport: check for open transport<commit_after>// Package glumelogger provides support for Flume NG in Go
//
// It communicates to a Flume NG agent in Thrift via TFramedTranport with TCompactProtocol
// thrift bindings are taken from Flume 1.7[1] (however they should be compatible with 1.6, 1.5, 1.4)
//
// [1] https://raw.githubusercontent.com/apache/flume/flume-1.7/flume-ng-sdk/src/main/thrift/flume.thrift
package glumelogger

import (
	"fmt"
	"log"
	"net"
	"os"
	"strconv"
	"sync"
	"time"

	"git.apache.org/thrift.git/lib/go/thrift"
	"github.com/ceocoder/glumelogger/flume"
)

// GlumeLogger holds thrift client and headers
// use NewGlumeLogger to create new one
type GlumeLogger struct {
	transport *thrift.TTransport
	client    *flume.ThriftSourceProtocolClient
	headers   *map[string]string
	lm        *sync.Mutex
	log       *log.Logger
}

// NewGlumeLogger create a new GlumeLogger client, it requires a host, port and
// map of headers
func NewGlumeLogger(host string, port int, headers *map[string]string) *GlumeLogger {
	var trans thrift.TTransport
	trans, err := thrift.NewTSocket(net.JoinHostPort(host, strconv.Itoa(port)))
	if err != nil {
		fmt.Fprintln(os.Stderr, "error resolving address:", err)
		os.Exit(1)
	}
	trans = thrift.NewTFramedTransport(trans)
	client := flume.NewThriftSourceProtocolClientFactory(trans, thrift.NewTCompactProtocolFactory())
	gl := &GlumeLogger{&trans, client, headers, &sync.Mutex{}, log.New(os.Stdout, "[GlumeLogger] ", log.Ldate|log.Ltime)}
	go gl.keepOpen()
	return gl
}

// keepOpen checks if transport is open every 2 seconds
// if transport is closed it will try to re-open it, if it can't it will die hard.
func (l *GlumeLogger) keepOpen() {
	for {
		if l.client.Transport.IsOpen() {
			time.Sleep(2 * time.Second)
		} else {
			err := l.client.Transport.Open()
			if err != nil {
				l.log.Fatalf("Flume Transport Closed, aborting: %v", err)
			}
		}
	}
}

// Log forwards message to be logged as array of bytes
// returns status of write and error
//
// append operation is wrapped in a mutex making it thread-safe.
func (l *GlumeLogger) Log(body []byte) (flume.Status, error) {

	event := &flume.ThriftFlumeEvent{Headers: *l.headers, Body: body}

	l.lm.Lock()
	defer l.lm.Unlock()
	if !l.client.Transport.IsOpen() {
		l.client.Transport.Open()
	}
	status, err := l.client.Append(event)
	if err != nil {
		// close bad transport proactively for the next write
		log.Printf("Error appending event: %v", err)
		l.client.Transport.Close()
		return status, err
	}

	return status, nil
}
<|endoftext|>