<commit_before>package hub

import (
	"errors"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/utrack/go-simple-chat/logger"
	"github.com/utrack/go-simple-chat/message"
	"strings"
	"testing"
	"time"
)

func TestHub(t *testing.T) {
	Convey("With hub", t, func() {
		h := NewHub(nil, nil, nil).(*hub)
		h.log = func(l logger.Level, f string, opts ...interface{}) {
		}

		// Run the pumps
		h.Run()

		Convey("Chans and structs should be initialized", func() {
			So(h.incomingDiscons, ShouldNotBeNil)
			So(h.incomingMsgs, ShouldNotBeNil)
			So(h.sessions, ShouldNotBeNil)
		})

		Convey("ClientExists should return false", func() {
			So(h.clientExists("foo bar"), ShouldBeFalse)
		})

		Convey("Client nickname sanitizer", func() {
			h.nameChecker = func(v string) (string, error) {
				if v == "should_fail" {
					return ``, errors.New("failed")
				}
				return "bar", nil
			}

			Convey("RegisterClient should fail with bad nick", func() {
				c1 := newClientMock()
				So(h.RegisterClient(c1, "should_fail"), ShouldNotBeNil)
			})
			Convey("Should accept new name from sanitizer", func() {
				c1 := newClientMock()
				So(h.RegisterClient(c1, "qwe"), ShouldBeNil)

				So(h.clientExists(`bar`), ShouldBeTrue)

				Convey("Should process the nick before checking for collisions", func() {
					So(h.RegisterClient(c1, `qwe`), ShouldEqual, ErrNickCollision)
				})
			})
		})

		Convey("Client should register successfully", func() {
			cName := "client1"
			c1 := newClientMock()
			So(h.RegisterClient(c1, cName), ShouldBeNil)

			// Flush EventJoin
			var got message.One
			select {
			case got = <-c1.acceptedMsgs:
			case <-time.After(time.Second):
				So(0, ShouldEqual, "client did not receive the EventJoin!")
			}
			So(got.Type, ShouldEqual, message.EventJoin)
			So(got.IsMuted, ShouldBeFalse)
			So(got.From, ShouldEqual, cName)

			Convey("ClientExists should return true", func() {
				So(h.clientExists(cName), ShouldBeTrue)
			})

			Convey("Double registering same nickname should fail", func() {
				So(h.RegisterClient(c1, cName), ShouldEqual, ErrNickCollision)
			})

			Convey("Client should be in the dict", func() {
				got, ok := h.sessions[cName]
				So(ok, ShouldBeTrue)
				So(got.c, ShouldResemble, c1)
			})

			Convey("Should deregister client on discon", func() {
				c1.discChan <- errors.New("test")
				<-time.After(time.Second / 2)
				So(h.clientExists(cName), ShouldBeFalse)
				_, ok := h.sessions[cName]
				So(ok, ShouldBeFalse)
			})

			Convey("Should route the message to the client", func() {
				msg := message.One{Type: message.EventMessage, IsMuted: true, From: "foo", Text: "barbaz"}
				h.incomingMsgs <- msg

				var got message.One
				So(got, ShouldNotResemble, msg)
				select {
				case got = <-c1.acceptedMsgs:
				case <-time.After(time.Second):
					So(0, ShouldEqual, "client did not receive the message!")
				}
				So(got, ShouldResemble, msg)
			})

			Convey("User should retrieve its own message", func() {
				msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
				c1.msgChan <- msg
				msg.From = cName

				var got message.One
				So(got, ShouldNotResemble, msg)
				select {
				case got = <-c1.acceptedMsgs:
				case <-time.After(time.Second):
					So(0, ShouldEqual, "client did not receive the message!")
				}
				So(got, ShouldResemble, msg)

				Convey("With sanitizer", func() {
					h.msgSanitizer = func(s string) string {
						return strings.Replace(s, `bar`, `foo`, -1)
					}

					Convey("Should sanitize the message's text", func() {
						msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
						c1.msgChan <- msg
						msg.From = cName
						msg.Text = `foobaz`

						var got message.One
						So(got, ShouldNotResemble, msg)
						select {
						case got = <-c1.acceptedMsgs:
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the message!")
						}
						So(got, ShouldResemble, msg)

					})

				})
			})

			Convey("With second client", func() {
				c2Name := "client2"
				c2 := newClientMock()
				So(h.RegisterClient(c2, c2Name), ShouldBeNil)

				Convey("Should receive own EventJoin", func() {
					// Flush EventJoin
					var got message.One
				ForOuter:
					for {
						select {
						case got = <-c2.acceptedMsgs:
							if got.From == c2Name {
								break ForOuter
							}
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the EventJoin!")
							break ForOuter
						}
					}
					So(got.Type, ShouldEqual, message.EventJoin)
					So(got.IsMuted, ShouldBeFalse)
					So(got.From, ShouldEqual, c2Name)

				})

				Convey("First one should receive the EventJoin", func() {
					var got message.One
					select {
					case got = <-c1.acceptedMsgs:
					case <-time.After(time.Second):
						So(0, ShouldEqual, "client did not receive the message!")
					}
					So(got.Type, ShouldEqual, message.EventJoin)
					So(got.From, ShouldEqual, c2Name)

					Convey("And nothing more", func() {
						var got message.One
						select {
						case got = <-c1.acceptedMsgs:
							So(got, ShouldEqual, "client received some message!")
						case <-time.After(time.Second):
						}
						So(got, ShouldResemble, message.One{})
					})
				})

				Convey("Second client should rcv muted EventJoins about existing users", func() {

					var got message.One
					select {
					case got = <-c2.acceptedMsgs:
					case <-time.After(time.Second):
						So(0, ShouldEqual, "client did not receive the EventJoin!")
					}
					So(got.Type, ShouldEqual, message.EventJoin)
					So(got.IsMuted, ShouldBeTrue)
					So(got.From, ShouldEqual, cName)
				})

				Convey("With EventJoins dumped", func() {
					select {
					case <-c1.acceptedMsgs:
					case <-time.After(time.Second):
					}

				ForOuter:
					for {
						select {
						case got = <-c2.acceptedMsgs:
						case <-time.After(time.Second / 2):
							break ForOuter
						}
					}

					Convey("First should receive EventLeave on second discon", func() {
						c2.discChan <- errors.New("test")

						var got message.One
						select {
						case got = <-c1.acceptedMsgs:
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the message!")
						}
						So(got.Type, ShouldEqual, message.EventLeave)
						So(got.From, ShouldEqual, c2Name)
						So(got.IsMuted, ShouldBeFalse)
						So(got.Text, ShouldEqual, "test")
					})

					Convey("Second should receive EventLeave on first discon", func() {
						c1.discChan <- errors.New("test")

						var got message.One
						select {
						case got = <-c2.acceptedMsgs:
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the message!")
						}
						So(got.Type, ShouldEqual, message.EventLeave)
						So(got.From, ShouldEqual, cName)
						So(got.IsMuted, ShouldBeFalse)
						So(got.Text, ShouldEqual, "test")
					})

					Convey("Clients should be able to chat", func() {
						Convey("First to second", func() {
							msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
							c1.msgChan <- msg
							msg.From = cName

							var got message.One
							So(got, ShouldNotResemble, msg)
							select {
							case got = <-c2.acceptedMsgs:
							case <-time.After(time.Second):
								So(0, ShouldEqual, "client did not receive the message!")
							}
							So(got, ShouldResemble, msg)
						})

						Convey("Second to first", func() {
							msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
							c2.msgChan <- msg
							msg.From = c2Name

							var got message.One
							So(got, ShouldNotResemble, msg)
							select {
							case got = <-c1.acceptedMsgs:
							case <-time.After(time.Second):
								So(0, ShouldEqual, "client did not receive the message!")
							}
							So(got, ShouldResemble, msg)
						})

					})

				})
			})
		})
	})
}

func TestLogging(t *testing.T) {
	Convey("With hub", t, func() {
		h := NewHub(nil, nil, nil).(*hub)

		// Run the pumps
		h.Run()

		Convey("With logfunc", func() {
			var lastLogLevel logger.Level
			var lastLogMsg string
			h.log = func(l logger.Level, f string, opts ...interface{}) {
				lastLogLevel = l
				lastLogMsg = f
			}

			Convey("On join", func() {
				cName := "client1"
				c1 := newClientMock()
				So(h.RegisterClient(c1, cName), ShouldBeNil)

				Convey("Should log successfully", func() {
					So(lastLogLevel, ShouldEqual, logger.LevelDebug)
					So(lastLogMsg, ShouldEqual, `User connected: %v`)
				})

				Convey("On leave", func() {
					c1.discChan <- errors.New("test")
					<-time.After(time.Second / 3)
					So(lastLogLevel, ShouldEqual, logger.LevelDebug)
					So(lastLogMsg, ShouldEqual, `User dropped: %v, reason %v`)

				})

			})
		})
	})
}
<commit_msg>Fixed data race in the Hub's tests<commit_after>package hub

import (
	"errors"
	. "github.com/smartystreets/goconvey/convey"
	"github.com/utrack/go-simple-chat/logger"
	"github.com/utrack/go-simple-chat/message"
	"strings"
	"testing"
	"time"
)

func TestHub(t *testing.T) {
	Convey("With hub", t, func() {
		h := NewHub(nil, nil, nil).(*hub)
		h.log = func(l logger.Level, f string, opts ...interface{}) {
		}

		// Run the pumps
		h.Run()

		Convey("Chans and structs should be initialized", func() {
			So(h.incomingDiscons, ShouldNotBeNil)
			So(h.incomingMsgs, ShouldNotBeNil)
			So(h.sessions, ShouldNotBeNil)
		})

		Convey("ClientExists should return false", func() {
			So(h.clientExists("foo bar"), ShouldBeFalse)
		})

		Convey("Client nickname sanitizer", func() {
			h.nameChecker = func(v string) (string, error) {
				if v == "should_fail" {
					return ``, errors.New("failed")
				}
				return "bar", nil
			}

			Convey("RegisterClient should fail with bad nick", func() {
				c1 := newClientMock()
				So(h.RegisterClient(c1, "should_fail"), ShouldNotBeNil)
			})
			Convey("Should accept new name from sanitizer", func() {
				c1 := newClientMock()
				So(h.RegisterClient(c1, "qwe"), ShouldBeNil)

				So(h.clientExists(`bar`), ShouldBeTrue)

				Convey("Should process the nick before checking for collisions", func() {
					So(h.RegisterClient(c1, `qwe`), ShouldEqual, ErrNickCollision)
				})
			})
		})

		Convey("Client should register successfully", func() {
			cName := "client1"
			c1 := newClientMock()
			So(h.RegisterClient(c1, cName), ShouldBeNil)

			// Flush EventJoin
			var got message.One
			select {
			case got = <-c1.acceptedMsgs:
			case <-time.After(time.Second):
				So(0, ShouldEqual, "client did not receive the EventJoin!")
			}
			So(got.Type, ShouldEqual, message.EventJoin)
			So(got.IsMuted, ShouldBeFalse)
			So(got.From, ShouldEqual, cName)

			Convey("ClientExists should return true", func() {
				So(h.clientExists(cName), ShouldBeTrue)
			})

			Convey("Double registering same nickname should fail", func() {
				So(h.RegisterClient(c1, cName), ShouldEqual, ErrNickCollision)
			})

			Convey("Client should be in the dict", func() {
				got, ok := h.sessions[cName]
				So(ok, ShouldBeTrue)
				So(got.c, ShouldResemble, c1)
			})

			Convey("Should deregister client on discon", func() {
				c1.discChan <- errors.New("test")
				<-time.After(time.Second / 2)
				So(h.clientExists(cName), ShouldBeFalse)
				_, ok := h.sessions[cName]
				So(ok, ShouldBeFalse)
			})

			Convey("Should route the message to the client", func() {
				msg := message.One{Type: message.EventMessage, IsMuted: true, From: "foo", Text: "barbaz"}
				h.incomingMsgs <- msg

				var got message.One
				So(got, ShouldNotResemble, msg)
				select {
				case got = <-c1.acceptedMsgs:
				case <-time.After(time.Second):
					So(0, ShouldEqual, "client did not receive the message!")
				}
				So(got, ShouldResemble, msg)
			})

			Convey("User should retrieve its own message", func() {
				msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
				c1.msgChan <- msg
				msg.From = cName

				var got message.One
				So(got, ShouldNotResemble, msg)
				select {
				case got = <-c1.acceptedMsgs:
				case <-time.After(time.Second):
					So(0, ShouldEqual, "client did not receive the message!")
				}
				So(got, ShouldResemble, msg)

				Convey("With sanitizer", func() {
					h.msgSanitizer = func(s string) string {
						return strings.Replace(s, `bar`, `foo`, -1)
					}

					Convey("Should sanitize the message's text", func() {
						msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
						c1.msgChan <- msg
						msg.From = cName
						msg.Text = `foobaz`

						var got message.One
						So(got, ShouldNotResemble, msg)
						select {
						case got = <-c1.acceptedMsgs:
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the message!")
						}
						So(got, ShouldResemble, msg)

					})

				})
			})

			Convey("With second client", func() {
				c2Name := "client2"
				c2 := newClientMock()
				So(h.RegisterClient(c2, c2Name), ShouldBeNil)

				Convey("Should receive own EventJoin", func() {
					// Flush EventJoin
					var got message.One
				ForOuter:
					for {
						select {
						case got = <-c2.acceptedMsgs:
							if got.From == c2Name {
								break ForOuter
							}
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the EventJoin!")
							break ForOuter
						}
					}
					So(got.Type, ShouldEqual, message.EventJoin)
					So(got.IsMuted, ShouldBeFalse)
					So(got.From, ShouldEqual, c2Name)

				})

				Convey("First one should receive the EventJoin", func() {
					var got message.One
					select {
					case got = <-c1.acceptedMsgs:
					case <-time.After(time.Second):
						So(0, ShouldEqual, "client did not receive the message!")
					}
					So(got.Type, ShouldEqual, message.EventJoin)
					So(got.From, ShouldEqual, c2Name)

					Convey("And nothing more", func() {
						var got message.One
						select {
						case got = <-c1.acceptedMsgs:
							So(got, ShouldEqual, "client received some message!")
						case <-time.After(time.Second):
						}
						So(got, ShouldResemble, message.One{})
					})
				})

				Convey("Second client should rcv muted EventJoins about existing users", func() {

					var got message.One
					select {
					case got = <-c2.acceptedMsgs:
					case <-time.After(time.Second):
						So(0, ShouldEqual, "client did not receive the EventJoin!")
					}
					So(got.Type, ShouldEqual, message.EventJoin)
					So(got.IsMuted, ShouldBeTrue)
					So(got.From, ShouldEqual, cName)
				})

				Convey("With EventJoins dumped", func() {
					select {
					case <-c1.acceptedMsgs:
					case <-time.After(time.Second):
					}

				ForOuter:
					for {
						select {
						case got = <-c2.acceptedMsgs:
						case <-time.After(time.Second / 2):
							break ForOuter
						}
					}

					Convey("First should receive EventLeave on second discon", func() {
						c2.discChan <- errors.New("test")

						var got message.One
						select {
						case got = <-c1.acceptedMsgs:
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the message!")
						}
						So(got.Type, ShouldEqual, message.EventLeave)
						So(got.From, ShouldEqual, c2Name)
						So(got.IsMuted, ShouldBeFalse)
						So(got.Text, ShouldEqual, "test")
					})

					Convey("Second should receive EventLeave on first discon", func() {
						c1.discChan <- errors.New("test")

						var got message.One
						select {
						case got = <-c2.acceptedMsgs:
						case <-time.After(time.Second):
							So(0, ShouldEqual, "client did not receive the message!")
						}
						So(got.Type, ShouldEqual, message.EventLeave)
						So(got.From, ShouldEqual, cName)
						So(got.IsMuted, ShouldBeFalse)
						So(got.Text, ShouldEqual, "test")
					})

					Convey("Clients should be able to chat", func() {
						Convey("First to second", func() {
							msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
							c1.msgChan <- msg
							msg.From = cName

							var got message.One
							So(got, ShouldNotResemble, msg)
							select {
							case got = <-c2.acceptedMsgs:
							case <-time.After(time.Second):
								So(0, ShouldEqual, "client did not receive the message!")
							}
							So(got, ShouldResemble, msg)
						})

						Convey("Second to first", func() {
							msg := message.One{Type: message.EventMessage, IsMuted: true, Text: "barbaz"}
							c2.msgChan <- msg
							msg.From = c2Name

							var got message.One
							So(got, ShouldNotResemble, msg)
							select {
							case got = <-c1.acceptedMsgs:
							case <-time.After(time.Second):
								So(0, ShouldEqual, "client did not receive the message!")
							}
							So(got, ShouldResemble, msg)
						})

					})

				})
			})
		})
	})
}

func TestLogging(t *testing.T) {
	Convey("With hub", t, func() {
		h := NewHub(nil, nil, nil).(*hub)

		// Run the pumps
		h.Run()

		Convey("With logfunc", func() {
			type LogMsg struct {
				Lvl logger.Level
				Msg string
			}
			logs := make(chan LogMsg, 10)
			h.log = func(l logger.Level, f string, opts ...interface{}) {
				logs <- LogMsg{Lvl: l, Msg: f}
			}

			Convey("On join", func() {
				cName := "client1"
				c1 := newClientMock()
				So(h.RegisterClient(c1, cName), ShouldBeNil)

				Convey("Should log successfully", func() {
					var logMsg LogMsg
					select {
					case logMsg = <-logs:
					case <-time.After(time.Second):
						So(0, ShouldEqual, "Logs were not sent!")
					}
					So(logMsg.Lvl, ShouldEqual, logger.LevelDebug)
					So(logMsg.Msg, ShouldEqual, `User connected: %v`)
				})

				Convey("On leave", func() {
					// Dump UserConnected
					<-logs
					c1.discChan <- errors.New("test")

					var logMsg LogMsg
					select {
					case logMsg = <-logs:
					case <-time.After(time.Second):
						So(0, ShouldEqual, "Logs were not sent!")
					}
					So(logMsg.Lvl, ShouldEqual, logger.LevelDebug)
					So(logMsg.Msg, ShouldEqual, `User dropped: %v, reason %v`)

				})

			})
		})
	})
}
<|endoftext|>


<commit_before>package datatable

import (
	"encoding/json"
	"errors"
)

var (
	IndexError  = errors.New("Index out of range")
	ValueError  = errors.New("Incorrect argument value")
	NumColError = errors.New("Incorrect number of columns")
)

// DataTable is an in-memory relational table.
// The data values are immutable.
type DataTable struct {
	columns [][]string
	nrow    int
	ncol    int
}

// NewDataTable creates a new data table with a given number of columns.
func NewDataTable(ncol int) *DataTable {
	columns := make([][]string, ncol)
	for y := range columns {
		columns[y] = make([]string, 0)
	}
	return &DataTable{
		columns: columns,
		nrow:    0,
		ncol:    ncol,
	}
}

func (dt *DataTable) NumRow() int {
	return dt.nrow
}

func (dt *DataTable) NumCol() int {
	return dt.ncol
}

// CheckRow returns an error if row x does not exist.
func (dt *DataTable) CheckRow(x int) error {
	if x < 0 || x >= dt.nrow {
		return IndexError
	}
	return nil
}

// CheckCol returns an error if column y does not exist.
func (dt *DataTable) CheckCol(y int) error {
	if y < 0 || y > dt.ncol {
		return IndexError
	}
	return nil
}

// AppendRow appends a new row at the bottom of the table.
func (dt *DataTable) AppendRow(row []string) error {
	if len(row) != dt.ncol {
		return NumColError
	}
	for x := range row {
		dt.columns[x] = append(dt.columns[x], row[x])
	}
	dt.nrow++
	return nil
}

// GetRow returns the row at index x.
func (dt *DataTable) GetRow(x int) ([]string, error) {
	if x < 0 || x >= dt.nrow {
		return nil, IndexError
	}
	row := make([]string, dt.ncol)
	for y := 0; y < dt.ncol; y++ {
		row[y] = dt.columns[y][x]
	}
	return row, nil
}

// ApplyColumn calls the function fn using all values in column y
// from top to bottom.
// fn takes two argument: the first is the row index and the second
// is the corresponding value.
// Error is returned immediately if encountered.
func (dt *DataTable) ApplyColumn(y int, fn func(int, string) error) error {
	if y < 0 || y > dt.ncol {
		return IndexError
	}
	for x, v := range dt.columns[y] {
		if err := fn(x, v); err != nil {
			return err
		}
	}
	return nil
}

// Slice take a contiguous subset of at most n rows, starting at index x,
// and make a new data table from them.
func (dt *DataTable) Slice(x, n int) (*DataTable, error) {
	if x < 0 || x >= dt.nrow {
		return nil, IndexError
	}
	if n < 0 {
		return nil, ValueError
	}
	end := x + n
	if end >= dt.nrow {
		end = dt.nrow
	}
	columns := make([][]string, dt.ncol)
	for y := range columns {
		columns[y] = dt.columns[y][x:end]
	}
	nrow := end - x
	return &DataTable{
		columns: columns,
		nrow:    nrow,
		ncol:    len(columns),
	}, nil
}

// Join performs relational join between the left and right tables.
// The join condition is defined by the function fn, which takes two rows,
// l and r, from the left and right tables respectively, and returns
// whether the two rows should be joined.
// The join result is returned as a new data table.
// Each joined rows contains all the fields from the input tables,
// in the order of [left table fields ... right table fields ...].
func Join(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	return joinTables(left, right, fn, false)
}

// LeftJoin is similar to Join, execpt that every row from the left table
// will be part of the join result even it doesn't join with any row from
// the right table.
// e.g., [left table fields ... empty fields]
// where the empty fields have the same number of columns as the right table.
func LeftJoin(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	return joinTables(left, right, fn, true)
}

func joinTables(left, right *DataTable, fn func(l, r []string) bool, leftJoin bool) *DataTable {
	out := make(chan []string)
	go func() {
		for i := 0; i < left.NumRow(); i++ {
			l, _ := left.GetRow(i)
			for j := 0; j < right.NumRow(); j++ {
				r, _ := right.GetRow(j)
				if fn(l, r) {
					out <- append(l, r...)
				}
			}
			if leftJoin {
				r := make([]string, right.NumCol())
				out <- append(l, r...)
			}
		}
		close(out)
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}

// Marshal data table into JSON.
func (dt *DataTable) MarshalJSON() ([]byte, error) {
	return json.Marshal(dt.columns)
}

// Unmarshal data table from JSON.
func (dt *DataTable) UnmarshalJSON(data []byte) error {
	var columns [][]string
	if err := json.Unmarshal(data, &columns); err != nil {
		return err
	}
	var nrow, ncol int
	ncol = len(columns)
	if ncol > 0 {
		nrow = len(columns[0])
		for y := 0; y < ncol; y++ {
			if len(columns[y]) != nrow {
				return NumColError
			}
		}
	}
	dt = &DataTable{
		columns: columns,
		nrow:    nrow,
		ncol:    ncol,
	}
	return nil
}
<commit_msg>use row-major internal<commit_after>package datatable

import (
	"encoding/json"
	"errors"
)

var (
	IndexError  = errors.New("Index out of range")
	ValueError  = errors.New("Incorrect argument value")
	NumColError = errors.New("Incorrect number of columns")
)

// DataTable is an in-memory relational table.
// The data values are immutable.
type DataTable struct {
	rows [][]string
	nrow int
	ncol int
}

// NewDataTable creates a new data table with a given number of columns.
func NewDataTable(ncol int) *DataTable {
	rows := make([][]string, 0)
	return &DataTable{
		rows: rows,
		nrow: 0,
		ncol: ncol,
	}
}

func (dt *DataTable) NumRow() int {
	return dt.nrow
}

func (dt *DataTable) NumCol() int {
	return dt.ncol
}

// CheckRow returns an error if row x does not exist.
func (dt *DataTable) CheckRow(x int) error {
	if x < 0 || x >= dt.nrow {
		return IndexError
	}
	return nil
}

// CheckCol returns an error if column y does not exist.
func (dt *DataTable) CheckCol(y int) error {
	if y < 0 || y > dt.ncol {
		return IndexError
	}
	return nil
}

// AppendRow appends a new row at the bottom of the table.
func (dt *DataTable) AppendRow(row []string) error {
	if len(row) != dt.ncol {
		return NumColError
	}
	dt.rows = append(dt.rows, row)
	dt.nrow++
	return nil
}

// GetRow returns the row at index x.
func (dt *DataTable) GetRow(x int) ([]string, error) {
	if err := dt.CheckRow(x); err != nil {
		return nil, err
	}
	row := make([]string, dt.ncol)
	copy(row, dt.rows[x])
	return row, nil
}

// ApplyColumn calls the function fn using all values in column y
// from top to bottom.
// fn takes two argument: the first is the row index and the second
// is the corresponding value.
// Error is returned immediately if encountered.
func (dt *DataTable) ApplyColumn(y int, fn func(int, string) error) error {
	if err := dt.CheckCol(y); err != nil {
		return err
	}
	for x, row := range dt.rows {
		if err := fn(x, row[y]); err != nil {
			return err
		}
	}
	return nil
}

// Slice take a contiguous subset of at most n rows, starting at index x,
// and make a new data table from them.
func (dt *DataTable) Slice(x, n int) (*DataTable, error) {
	if x < 0 || x >= dt.nrow {
		return nil, IndexError
	}
	if n < 0 {
		return nil, ValueError
	}
	end := x + n
	if end >= dt.nrow {
		end = dt.nrow
	}
	rows := dt.rows[x:end]
	return &DataTable{
		rows: rows,
		nrow: len(rows),
		ncol: dt.ncol,
	}, nil
}

// Marshal data table into JSON.
func (dt *DataTable) MarshalJSON() ([]byte, error) {
	return json.Marshal(dt.rows)
}

// Unmarshal data table from JSON.
func (dt *DataTable) UnmarshalJSON(data []byte) error {
	var rows [][]string
	if err := json.Unmarshal(data, &rows); err != nil {
		return err
	}
	var nrow, ncol int
	nrow = len(rows)
	if nrow > 0 {
		ncol = len(rows[0])
		for x := range rows {
			if len(rows[x]) != ncol {
				return NumColError
			}
		}
	}
	dt = &DataTable{
		rows: rows,
		nrow: nrow,
		ncol: ncol,
	}
	return nil
}

// Join performs relational join between the left and right tables.
// The join condition is defined by the function fn, which takes two rows,
// l and r, from the left and right tables respectively, and returns
// whether the two rows should be joined.
// The join result is returned as a new data table.
// Each joined rows contains all the fields from the input tables,
// in the order of [left table fields ... right table fields ...].
func Join(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	return joinTables(left, right, fn, false)
}

// LeftJoin is similar to Join, execpt that every row from the left table
// will be part of the join result even it doesn't join with any row from
// the right table.
// e.g., [left table fields ... empty fields]
// where the empty fields have the same number of columns as the right table.
func LeftJoin(left, right *DataTable, fn func(l, r []string) bool) *DataTable {
	return joinTables(left, right, fn, true)
}

func joinTables(left, right *DataTable, fn func(l, r []string) bool, leftJoin bool) *DataTable {
	out := make(chan []string)
	go func() {
		for i := 0; i < left.NumRow(); i++ {
			l, _ := left.GetRow(i)
			for j := 0; j < right.NumRow(); j++ {
				r, _ := right.GetRow(j)
				if fn(l, r) {
					out <- append(l, r...)
				}
			}
			if leftJoin {
				r := make([]string, right.NumCol())
				out <- append(l, r...)
			}
		}
		close(out)
	}()
	joined := NewDataTable(left.NumCol() + right.NumCol())
	for row := range out {
		joined.AppendRow(row)
	}
	return joined
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
)

func hasVowels(s string) bool {
	var cnt int
	for _, c := range s {
		switch c {
		case 'a', 'e', 'i', 'o', 'u':
			cnt++
			if cnt >= 3 {
				return true
			}
		}
	}

	return false
}

func hasRepeated(s string) bool {
	r := []rune(s)
	for i := 0; i < len(r)-1; i++ {
		if r[i+1] == r[i] {
			return true
		}
	}

	return false
}

func hasNoBad(s string) bool {
	r := []rune(s)
	for i := 0; i < len(r)-1; i++ {
		switch string(r[i : i+2]) {
		case "ab", "cd", "pq", "xy":
			return false
		}
	}

	return true
}

func hasRepeatedPair(s string) bool {
	return false
}

func hasRepeatWithGap(s string) bool {
	return false
}

func checkString(s string) bool {
	return hasVowels(s) && hasRepeated(s) && hasNoBad(s)
}

func checkString2(s string) bool {
	return hasRepeatedPair(s) && hasRepeatWithGap(s)
}

func run() int {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "%s filename\n", os.Args[0])
		return 1
	}

	f, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}
	defer f.Close()

	nice := 0
	nice2 := 0
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		s := scanner.Text()

		if checkString(s) {
			nice++
		}

		if checkString2(s) {
			nice2++
		}
	}

	fmt.Printf("nice: %d\nnice2: %d\n", nice, nice2)
	return 0
}

func main() {
	os.Exit(run())
}
<commit_msg>day5 part 2 complete<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
)

func hasVowels(s string) bool {
	var cnt int
	for _, c := range s {
		switch c {
		case 'a', 'e', 'i', 'o', 'u':
			cnt++
			if cnt >= 3 {
				return true
			}
		}
	}

	return false
}

func hasRepeated(s string) bool {
	r := []rune(s)
	for i := 0; i < len(r)-1; i++ {
		if r[i+1] == r[i] {
			return true
		}
	}

	return false
}

func hasNoBad(s string) bool {
	r := []rune(s)
	for i := 0; i < len(r)-1; i++ {
		switch string(r[i : i+2]) {
		case "ab", "cd", "pq", "xy":
			return false
		}
	}

	return true
}

func hasRepeatedPair(s string) bool {
	r := []rune(s)
	for i := 0; i < len(r)-3; i++ {
		for j := i + 2; j < len(r)-1; j++ {
			if r[j] == r[i] && r[j+1] == r[i+1] {
				return true
			}
		}
	}

	return false
}

func hasRepeatWithGap(s string) bool {
	r := []rune(s)
	for i := 0; i < len(r)-2; i++ {
		if r[i] == r[i+2] {
			return true
		}
	}

	return false
}

func checkString(s string) bool {
	return hasVowels(s) && hasRepeated(s) && hasNoBad(s)
}

func checkString2(s string) bool {
	return hasRepeatedPair(s) && hasRepeatWithGap(s)
}

func run() int {
	if len(os.Args) != 2 {
		fmt.Fprintf(os.Stderr, "%s filename\n", os.Args[0])
		return 1
	}

	f, err := os.Open(os.Args[1])
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return 1
	}
	defer f.Close()

	nice := 0
	nice2 := 0
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		s := scanner.Text()

		if checkString(s) {
			nice++
		}

		if checkString2(s) {
			nice2++
		}
	}

	fmt.Printf("nice: %d\nnice2: %d\n", nice, nice2)
	return 0
}

func main() {
	os.Exit(run())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/build"
	"os"
	"strings"
)

func operationClean(pkg *build.Package) {
	for _, filename := range pkg.GoFiles {
		if strings.HasSuffix(filename, ".rice-box.go") || strings.HasSuffix(filename, ".rice-single.go") {
			err := os.Remove(filename)
			if err != nil {
				fmt.Printf("error removing file (%s): %s\n", filename, err)
				os.Exit(-1)
			}
		}
	}
}
<commit_msg>Add verbose logging for clean operation<commit_after>package main

import (
	"fmt"
	"go/build"
	"os"
	"strings"
)

func operationClean(pkg *build.Package) {
	for _, filename := range pkg.GoFiles {
		verbosef("checking file '%s'\n", filename)
		if strings.HasSuffix(filename, ".rice-box.go") || strings.HasSuffix(filename, ".rice-single.go") {
			err := os.Remove(filename)
			if err != nil {
				fmt.Printf("error removing file (%s): %s\n", filename, err)
				os.Exit(-1)
			}
			verbosef("removed file '%s'\n", filename)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

var (
	srcCoord = Location{Coordinate: Coordinate{Latitude: 62.24027, Longitude: 25.74444}, Address: Address{Country: "Finland"}}
	trgCoord = Location{Coordinate: Coordinate{Latitude: 60.45138, Longitude: 22.26666}, Address: Address{Country: "Finland"}}
	noCoords = []Edge{
		{Source: Location{Address: Address{Street: "Erottaja", City: "Helsinki", Country: "Finland"}}, Target: Location{Address: Address{Street: "Esplanadi", City: "Helsinki", Country: "Finland"}}},
	}

	noCoordsFinland = []Edge{
		{Source: Location{Address: Address{Street: "Erottaja", City: "Helsinki", Country: "Finland"}}, Target: Location{Address: Address{Street: "Esplanadi", City: "Helsinki", Country: "Finland"}}},
		{Source: Location{Address: Address{Street: "Taitoniekantie", City: "Jyväskylä", Country: "Finland"}}, Target: Location{Address: Address{Street: "Vuolteenkatu", City: "Tampere", Country: "Finland"}}},
		{Source: Location{Address: Address{Street: "Vuolteenkatu", City: "Tampere", Country: "Finland"}}, Target: Location{Address: Address{Street: "Hikiäntie", City: "Riihimäki", Country: "Finland"}}},
	}

	coordsGermany = []Edge{
		{Source: Location{Coordinate: Coordinate{Latitude: 49.0811, Longitude: 9.8795}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 49.3636, Longitude: 10.1472}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 49.4413, Longitude: 9.7394}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 48.4356, Longitude: 12.4750}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 47.9074, Longitude: 10.4459}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 48.4133, Longitude: 9.2127}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 50.1690, Longitude: 11.2960}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 52.4292, Longitude: 13.2286}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 52.5876, Longitude: 13.2461}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 53.4989, Longitude: 13.9805}, Address: Address{Country: "Germany"}}},
	}
)

func BenchmarkFinlandPathsExtraction(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, noCoordsFinland})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkGermanyPathsExtraction(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, coordsGermany})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func TestCalculateCoordinatePathWithCoordinates(t *testing.T) {
	if _, err := CalculateCoordinatePathFromAddresses(server.Config, srcCoord, trgCoord, 100); err != nil {
		t.Fatal(err)
	}
}

func TestCalculatePathsWithoutCoordinates(t *testing.T) {
	_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, noCoordsFinland})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Finland (%d edges) ok.", len(noCoordsFinland))

	_, err = CalculateCoordinatePaths(server.Config, PathsInput{100, coordsGermany})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Germany (%d edges) ok.", len(coordsGermany))
}

func BenchmarkFinlandCoordinatelessPathExtraction(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, noCoordsFinland})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func api_coordinate_query(t *testing.T, edges []Edge, speed_profile int) []CoordinatePath {
	payload := struct {
		SpeedProfile int
		Edges        []Edge
	}{
		speed_profile,
		edges,
	}

	jsonPayload, err := json.Marshal(payload)
	b := strings.NewReader(string(jsonPayload))

	var paths []CoordinatePath

	request := fmt.Sprintf("http://localhost:%d/cpaths", server.Config.Port)
	response, err := http.Post(request, "application/json", b)
	if err != nil {
		t.Fatal(response)
	} else if response.StatusCode != 200 {
		cont, _ := ioutil.ReadAll(response.Body)
		t.Fatal(string(cont))
	} else {
		defer response.Body.Close()
		cont, err := ioutil.ReadAll(response.Body)
		if err != nil {
			t.Fatal(err)
		}

		if err := json.Unmarshal(cont, &paths); err != nil {
			t.Logf(err.Error())
		}

		t.Logf("sent %d paths <=> got %d back", len(edges), len(paths))

		for i, p := range paths {
			t.Logf("Calculated path of %d m / %d secs from %s, %s, %s to %s, %s, %s", p.Distance, p.Time,
				edges[i].Source.Address.Street, edges[i].Source.Address.City, edges[i].Source.Address.Country,
				edges[i].Target.Address.Street, edges[i].Target.Address.City, edges[i].Target.Address.Country)
		}

		t.Logf("Calculated %d paths", len(paths))
	}

	return paths
}

func TestAPICalculateCoordinatePathWithExplicitCoordinates(t *testing.T) {
	api_coordinate_query(t, []Edge{{srcCoord, trgCoord}}, 100)
}

func TestAPICalculateCoordinatePathWithoutCoordinates(t *testing.T) {
	api_coordinate_query(t, noCoordsFinland, 100)
}
<commit_msg>Remove non-working tests.<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"testing"
)

var (
	srcCoord = Location{Coordinate: Coordinate{Latitude: 62.24027, Longitude: 25.74444}, Address: Address{Country: "Finland"}}
	trgCoord = Location{Coordinate: Coordinate{Latitude: 60.45138, Longitude: 22.26666}, Address: Address{Country: "Finland"}}
	noCoords = []Edge{
		{Source: Location{Address: Address{Street: "Erottaja", City: "Helsinki", Country: "Finland"}}, Target: Location{Address: Address{Street: "Esplanadi", City: "Helsinki", Country: "Finland"}}},
	}

	noCoordsFinland = []Edge{
		{Source: Location{Address: Address{Street: "Erottaja", City: "Helsinki", Country: "Finland"}}, Target: Location{Address: Address{Street: "Esplanadi", City: "Helsinki", Country: "Finland"}}},
		{Source: Location{Address: Address{Street: "Taitoniekantie", City: "Jyväskylä", Country: "Finland"}}, Target: Location{Address: Address{Street: "Vuolteenkatu", City: "Tampere", Country: "Finland"}}},
		{Source: Location{Address: Address{Street: "Vuolteenkatu", City: "Tampere", Country: "Finland"}}, Target: Location{Address: Address{Street: "Hikiäntie", City: "Riihimäki", Country: "Finland"}}},
	}

	coordsGermany = []Edge{
		{Source: Location{Coordinate: Coordinate{Latitude: 49.0811, Longitude: 9.8795}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 49.3636, Longitude: 10.1472}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 49.4413, Longitude: 9.7394}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 48.4356, Longitude: 12.4750}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 47.9074, Longitude: 10.4459}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 48.4133, Longitude: 9.2127}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 50.1690, Longitude: 11.2960}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 52.4292, Longitude: 13.2286}, Address: Address{Country: "Germany"}}},
		{Source: Location{Coordinate: Coordinate{Latitude: 52.5876, Longitude: 13.2461}, Address: Address{Country: "Germany"}}, Target: Location{Coordinate: Coordinate{Latitude: 53.4989, Longitude: 13.9805}, Address: Address{Country: "Germany"}}},
	}
)

func BenchmarkFinlandPathsExtraction(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, noCoordsFinland})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func BenchmarkGermanyPathsExtraction(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, coordsGermany})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func TestCalculatePathsWithoutCoordinates(t *testing.T) {
	_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, noCoordsFinland})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Finland (%d edges) ok.", len(noCoordsFinland))

	_, err = CalculateCoordinatePaths(server.Config, PathsInput{100, coordsGermany})

	if err != nil {
		t.Fatal(err)
	}

	t.Logf("Germany (%d edges) ok.", len(coordsGermany))
}

func BenchmarkFinlandCoordinatelessPathExtraction(b *testing.B) {
	for i := 0; i < b.N; i++ {
		_, err := CalculateCoordinatePaths(server.Config, PathsInput{100, noCoordsFinland})
		if err != nil {
			b.Fatal(err)
		}
	}
}

func api_coordinate_query(t *testing.T, edges []Edge, speed_profile int) []CoordinatePath {
	payload := struct {
		SpeedProfile int
		Edges        []Edge
	}{
		speed_profile,
		edges,
	}

	jsonPayload, err := json.Marshal(payload)
	b := strings.NewReader(string(jsonPayload))

	var paths []CoordinatePath

	request := fmt.Sprintf("http://localhost:%d/paths", server.Config.Port)
	response, err := http.Post(request, "application/json", b)
	if err != nil {
		t.Fatal(response)
	} else if response.StatusCode != 200 {
		cont, _ := ioutil.ReadAll(response.Body)
		t.Fatal(string(cont))
	} else {
		defer response.Body.Close()
		cont, err := ioutil.ReadAll(response.Body)
		if err != nil {
			t.Fatal(err)
		}

		if err := json.Unmarshal(cont, &paths); err != nil {
			t.Logf(err.Error())
		}

		t.Logf("sent %d paths <=> got %d back", len(edges), len(paths))

		for i, p := range paths {
			t.Logf("Calculated path of %d m / %d secs from %s, %s, %s to %s, %s, %s", p.Distance, p.Time,
				edges[i].Source.Address.Street, edges[i].Source.Address.City, edges[i].Source.Address.Country,
				edges[i].Target.Address.Street, edges[i].Target.Address.City, edges[i].Target.Address.Country)
		}

		t.Logf("Calculated %d paths", len(paths))
	}

	return paths
}

func TestAPICalculateCoordinatePathWithExplicitCoordinates(t *testing.T) {
	api_coordinate_query(t, []Edge{{srcCoord, trgCoord}}, 100)
}

func TestAPICalculateCoordinatePathWithoutCoordinates(t *testing.T) {
	api_coordinate_query(t, noCoordsFinland, 100)
}
<|endoftext|>


<commit_before>package main

// User user resource
type User struct {
	ID        string `json:"id"`
	LastName  string `json:"lastName"`
	FirstName string `json:"firstName"`
	Age       int    `json:"age"`
}

// Todo todo resource
type Todo struct {
	User  *User    `json:"user"`
	Title string   `json:"title"`
	Tags  []string `json:"tags"`
}

// UserUpdateRequest user update response
type UserUpdateRequest *User

// UserUpdateResponse user update response
type UserUpdateResponse *User

// TodoCreateRequest todo create request
type TodoCreateRequest struct {
	UserID string   `json:"userId"`
	Title  string   `json:"title"`
	Tags   []string `json:"tags"`
}
<commit_msg>Remove sample.go<commit_after><|endoftext|>


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

package doc

import (
	"go/token"
	"os"
	"time"
)

// Value represents constants and variable
type Value struct {
	Name          string // Value name.
	Doc           string
	Decl, FmtDecl string // Normal and formatted form of declaration.
	URL           string // VCS URL.
}

// Func represents functions
type Func struct {
	Name          string // Function name.
	Doc           string
	Decl, FmtDecl string // Normal and formatted form of declaration.
	URL           string // VCS URL.
	Code          string // Source code.
}

// Type represents structs and interfaces
type Type struct {
	Name          string // Type name.
	Doc           string
	Decl, FmtDecl string  // Normal and formatted form of declaration.
	URL           string  // VCS URL.
	Funcs         []*Func // Exported functions that return this type.
	IFuncs        []*Func // Internal functions that return this type.
	Methods       []*Func // Exported methods.
	IMethods      []*Func // Internal methods.
}

// PACKAGE_VER is modified when previously stored packages are invalid.
const PACKAGE_VER = "3"

// Package represents full information and documentation for a package.
type Package struct {
	// Package import path.
	ImportPath string

	// Synopsis and full documentation for package.
	Synopsis string
	Doc      string

	// Name of the project.
	ProjectName string

	// True if package documentation is incomplete.
	Truncated bool

	// Environment.
	GOOS, GOARCH string

	// Time when information last updated.
	Created time.Time

	Views, ViewedTime int64 // User viewed time(Unix-timestamp).

	Etag, Tags string // Revision tag and project tags.

	// Top-level declarations.
	Consts []*Value
	Funcs  []*Func
	Types  []*Type
	Vars   []*Value
	TDoc   map[string]string // Documentation for top-level declarations in Chinese.

	// Internal declarations.
	Iconsts []*Value
	Ifuncs  []*Func
	Itypes  []*Type
	Ivars   []*Value
	IDoc    map[string]string // Documentation for internal declarations in Chinese.

	Notes            []string // Source code notes.
	Files, TestFiles []string // Source files.
	Dirs             []string // Subdirectories

	Imports, TestImports []string // Imports.

	ImportedNum int    // Number of packages that imports this project.
	ImportPid   string // Packages id of packages that imports this project.
}

// source is source code file.
type source struct {
	name      string
	browseURL string
	rawURL    string
	data      []byte
}

func (s *source) Name() string       { return s.name }
func (s *source) Size() int64        { return int64(len(s.data)) }
func (s *source) Mode() os.FileMode  { return 0 }
func (s *source) ModTime() time.Time { return time.Time{} }
func (s *source) IsDir() bool        { return false }
func (s *source) Sys() interface{}   { return nil }

// walker holds the state used when building the documentation.
type walker struct {
	lineFmt  string
	pdoc     *Package
	srcLines map[string][]string // Source files with line arrays.
	srcs     map[string]*source  // Source files.
	fset     *token.FileSet
	buf      []byte // scratch space for printNode method.
}
<commit_msg>add box to content that from readme, fixed small bugs<commit_after>// Copyright 2011 Gary Burd
// Copyright 2013 Unknown
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package doc

import (
	"go/token"
	"os"
	"time"
)

// Value represents constants and variable
type Value struct {
	Name          string // Value name.
	Doc           string
	Decl, FmtDecl string // Normal and formatted form of declaration.
	URL           string // VCS URL.
}

// Func represents functions
type Func struct {
	Name          string // Function name.
	Doc           string
	Decl, FmtDecl string // Normal and formatted form of declaration.
	URL           string // VCS URL.
	Code          string // Source code.
}

// Type represents structs and interfaces
type Type struct {
	Name          string // Type name.
	Doc           string
	Decl, FmtDecl string  // Normal and formatted form of declaration.
	URL           string  // VCS URL.
	Funcs         []*Func // Exported functions that return this type.
	IFuncs        []*Func // Internal functions that return this type.
	Methods       []*Func // Exported methods.
	IMethods      []*Func // Internal methods.
}

// PACKAGE_VER is modified when previously stored packages are invalid.
const PACKAGE_VER = "4"

// Package represents full information and documentation for a package.
type Package struct {
	// Package import path.
	ImportPath string

	// Synopsis and full documentation for package.
	Synopsis string
	Doc      string

	// Name of the project.
	ProjectName string

	// True if package documentation is incomplete.
	Truncated bool

	// Environment.
	GOOS, GOARCH string

	// Time when information last updated.
	Created time.Time

	Views, ViewedTime int64 // User viewed time(Unix-timestamp).

	Etag, Tags string // Revision tag and project tags.

	// Top-level declarations.
	Consts []*Value
	Funcs  []*Func
	Types  []*Type
	Vars   []*Value
	TDoc   map[string]string // Documentation for top-level declarations in Chinese.

	// Internal declarations.
	Iconsts []*Value
	Ifuncs  []*Func
	Itypes  []*Type
	Ivars   []*Value
	IDoc    map[string]string // Documentation for internal declarations in Chinese.

	Notes            []string // Source code notes.
	Files, TestFiles []string // Source files.
	Dirs             []string // Subdirectories

	Imports, TestImports []string // Imports.

	ImportedNum int    // Number of packages that imports this project.
	ImportPid   string // Packages id of packages that imports this project.
}

// source is source code file.
type source struct {
	name      string
	browseURL string
	rawURL    string
	data      []byte
}

func (s *source) Name() string       { return s.name }
func (s *source) Size() int64        { return int64(len(s.data)) }
func (s *source) Mode() os.FileMode  { return 0 }
func (s *source) ModTime() time.Time { return time.Time{} }
func (s *source) IsDir() bool        { return false }
func (s *source) Sys() interface{}   { return nil }

// walker holds the state used when building the documentation.
type walker struct {
	lineFmt  string
	pdoc     *Package
	srcLines map[string][]string // Source files with line arrays.
	srcs     map[string]*source  // Source files.
	fset     *token.FileSet
	buf      []byte // scratch space for printNode method.
}
<|endoftext|>


<commit_before>// Copyright (c) 2016, M Bogus.
// This source file is part of the KUBE-AMQP-AUTOSCALE open source project
// Licensed under Apache License v2.0
// See LICENSE file for license information

// +build integration

package main

import (
	"errors"
	"os"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/streadway/amqp"
)

func TestGetQueueLengthNonExistentHost(t *testing.T) {
	_, err := getQueueLength("amqp://non-existent-host//", "")
	if err == nil {
		t.Fatalf("Error expected")
	}
	if got, want := err.Error(), "dial tcp: lookup non-existent-host: no such host"; !strings.HasPrefix(got, want) {
		t.Errorf("Expected err='%s', got: '%s'", want, got)
	}
}

func TestGetQueueLengthNonExistentQueue(t *testing.T) {
	_, err := getQueueLength(amqpURI(), "non-existent-queue")
	if err == nil {
		t.Fatalf("Error expected")
	}
	if got, want := err.Error(), `Exception (404) Reason: "NOT_FOUND - no queue 'non-existent-queue' in vhost '/'"`; got != want {
		t.Errorf("Expected err='%s', got: '%s'", want, got)
	}
}

func TestGetQueueLengthNoMsgs(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	msgs, err := getQueueLength(amqpURI(), tmpQ.Name)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := msgs, 0; got != want {
		t.Errorf("Expected %d, got: %d", want, got)
	}

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestGetQueueLength(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	for i := 0; i < 10; i++ {
		err = ch.Publish(
			"",        // exchange
			tmpQ.Name, // routing key
			false,     // mandatory
			false,     // immediate
			amqp.Publishing{
				ContentType:   "text/plain",
				Body:          []byte(strconv.Itoa(i)),
				CorrelationId: strconv.Itoa(i),
				DeliveryMode:  amqp.Persistent,
			})
		if err != nil {
			t.Fatal(err)
		}
	}

	msgs, err := getQueueLength(amqpURI(), tmpQ.Name)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := msgs, 10; got != want {
		t.Errorf("Expected %d, got: %d", want, got)
	}

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestMonitorQueueClosedChannel(t *testing.T) {
	forever := make(chan struct{}, 1)
	close(forever)

	f := func(i int) error {
		t.Fatalf("Unexpected result %d", i)
		return nil
	}

	monitorQueue("", "", 1, f, forever)
}

func TestMonitorQueue(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	for i := 0; i < 10; i++ {
		err = ch.Publish(
			"",        // exchange
			tmpQ.Name, // routing key
			false,     // mandatory
			false,     // immediate
			amqp.Publishing{
				ContentType:   "text/plain",
				Body:          []byte(strconv.Itoa(i)),
				CorrelationId: strconv.Itoa(i),
				DeliveryMode:  amqp.Persistent,
			})
		if err != nil {
			t.Fatal(err)
		}
	}

	forever := make(chan struct{}, 1)

	f := func(i int) error {
		if got, want := i, 10; got != want {
			t.Errorf("Expected %d, got: %d", want, got)
		}
		close(forever)
		return nil
	}

	monitorQueue(amqpURI(), tmpQ.Name, 1, f, forever)

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestMonitorQueueGetQueueLengthError(t *testing.T) {

	forever := make(chan struct{}, 1)

	f := func(i int) error {
		t.Fatalf("Unexpected result %d", i)
		return nil
	}

	go monitorQueue("amqp://non-existent-host//", "no-queue", 1, f, forever)

	time.Sleep(3 * time.Second)
	close(forever)

}

func TestMonitorQueueSaveStatError(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	forever := make(chan struct{}, 1)

	f := func(i int) error {
		if got, want := i, 0; got != want {
			t.Errorf("Expected %d, got: %d", want, got)
		}
		return errors.New("Dummy error")
	}

	go monitorQueue(amqpURI(), tmpQ.Name, 1, f, forever)

	time.Sleep(3 * time.Second)
	close(forever)

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func amqpURI() string { return os.Getenv("AMQP_URI") }
<commit_msg>Update test case for varying underlying error messages<commit_after>// Copyright (c) 2016, M Bogus.
// This source file is part of the KUBE-AMQP-AUTOSCALE open source project
// Licensed under Apache License v2.0
// See LICENSE file for license information

// +build integration

package main

import (
	"errors"
	"os"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/streadway/amqp"
)

func TestGetQueueLengthNonExistentHost(t *testing.T) {
	_, err := getQueueLength("amqp://non-existent-host//", "")
	if err == nil {
		t.Fatalf("Error expected")
	}
	if got, want := err.Error(), "dial tcp: lookup non-existent-host"; !strings.HasPrefix(got, want) {
		t.Errorf("Expected err='%s', got: '%s'", want, got)
	}
}

func TestGetQueueLengthNonExistentQueue(t *testing.T) {
	_, err := getQueueLength(amqpURI(), "non-existent-queue")
	if err == nil {
		t.Fatalf("Error expected")
	}
	if got, want := err.Error(), `Exception (404) Reason: "NOT_FOUND - no queue 'non-existent-queue' in vhost '/'"`; got != want {
		t.Errorf("Expected err='%s', got: '%s'", want, got)
	}
}

func TestGetQueueLengthNoMsgs(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	msgs, err := getQueueLength(amqpURI(), tmpQ.Name)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := msgs, 0; got != want {
		t.Errorf("Expected %d, got: %d", want, got)
	}

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestGetQueueLength(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	for i := 0; i < 10; i++ {
		err = ch.Publish(
			"",        // exchange
			tmpQ.Name, // routing key
			false,     // mandatory
			false,     // immediate
			amqp.Publishing{
				ContentType:   "text/plain",
				Body:          []byte(strconv.Itoa(i)),
				CorrelationId: strconv.Itoa(i),
				DeliveryMode:  amqp.Persistent,
			})
		if err != nil {
			t.Fatal(err)
		}
	}

	msgs, err := getQueueLength(amqpURI(), tmpQ.Name)
	if err != nil {
		t.Fatal(err)
	}
	if got, want := msgs, 10; got != want {
		t.Errorf("Expected %d, got: %d", want, got)
	}

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestMonitorQueueClosedChannel(t *testing.T) {
	forever := make(chan struct{}, 1)
	close(forever)

	f := func(i int) error {
		t.Fatalf("Unexpected result %d", i)
		return nil
	}

	monitorQueue("", "", 1, f, forever)
}

func TestMonitorQueue(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	for i := 0; i < 10; i++ {
		err = ch.Publish(
			"",        // exchange
			tmpQ.Name, // routing key
			false,     // mandatory
			false,     // immediate
			amqp.Publishing{
				ContentType:   "text/plain",
				Body:          []byte(strconv.Itoa(i)),
				CorrelationId: strconv.Itoa(i),
				DeliveryMode:  amqp.Persistent,
			})
		if err != nil {
			t.Fatal(err)
		}
	}

	forever := make(chan struct{}, 1)

	f := func(i int) error {
		if got, want := i, 10; got != want {
			t.Errorf("Expected %d, got: %d", want, got)
		}
		close(forever)
		return nil
	}

	monitorQueue(amqpURI(), tmpQ.Name, 1, f, forever)

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func TestMonitorQueueGetQueueLengthError(t *testing.T) {

	forever := make(chan struct{}, 1)

	f := func(i int) error {
		t.Fatalf("Unexpected result %d", i)
		return nil
	}

	go monitorQueue("amqp://non-existent-host//", "no-queue", 1, f, forever)

	time.Sleep(3 * time.Second)
	close(forever)

}

func TestMonitorQueueSaveStatError(t *testing.T) {
	conn, err := amqp.Dial(amqpURI())
	if err != nil {
		t.Fatal(err)
	}
	defer conn.Close()
	ch, err := conn.Channel()
	if err != nil {
		t.Fatal(err)
	}
	defer ch.Close()

	tmpQ, err := ch.QueueDeclare(
		"",    // name
		false, // durable
		true,  // delete when usused
		false, // exclusive
		false, // noWait
		nil,   // arguments
	)

	if err != nil {
		t.Fatal(err)
	}

	forever := make(chan struct{}, 1)

	f := func(i int) error {
		if got, want := i, 0; got != want {
			t.Errorf("Expected %d, got: %d", want, got)
		}
		return errors.New("Dummy error")
	}

	go monitorQueue(amqpURI(), tmpQ.Name, 1, f, forever)

	time.Sleep(3 * time.Second)
	close(forever)

	_, err = ch.QueueDelete(tmpQ.Name, false, false, true)
	if err != nil {
		t.Fatal(err)
	}
}

func amqpURI() string { return os.Getenv("AMQP_URI") }
<|endoftext|>


<commit_before>package main;

import(
    "fmt"
    "sort"
    "strings"
)

type Analytics struct {
    key string
    rows int
    encounters int
    sum int
    max int
    min int
    subkeys map[string]struct{} // map used as a set
}

func NewAnalytics(key string) *Analytics {
    a := new(Analytics);

    a.key = key;
    a.rows = 0;
    a.encounters = 0;
    a.sum = 0;
    a.max = 0;
    a.min = 9999999999; // TODO: find a better way to represent max/min

    return a;
}

func (a *Analytics) NewRow(data map[string]interface{}) {
    a.rows += 1;

    watchedValue := nestedGet(a.key, data);

    switch casted := watchedValue.(type) {
        case int:
            a.numberEncountered(casted);
        case float64:
            a.numberEncountered(int(casted));
        case map[string]interface{}:
            a.mapEncountered(casted);
    }
}

func (a *Analytics) numberEncountered(encountered int) {
    a.encounters += 1;
    a.sum += encountered;

    if(a.max < encountered) {
        a.max = encountered;
    }

    if(a.min > encountered) {
        a.min = encountered;
    }
}

func (a *Analytics) mapEncountered(encountered map[string]interface{}) {
    a.encounters += 1;
    fmt.Sprintf("%q", encountered);
    // fmt.Println("not a leaf node!")

    if len(a.subkeys) == 0 {
        // make map and populate
        a.subkeys = make(map[string]struct{}, len(encountered))
        for k := range encountered {
            a.subkeys[k] = struct{}{}
        }
    } else {
        // iterate map, append to subkeys as necessary
        for k := range encountered {
            // check if subkeys contains k
            if _, inSet := a.subkeys[k]; !inSet {
                // fmt.Println("Found new subkey:", k)
                a.subkeys[k] = struct{}{}
            }
        }
    }
}

func nestedGet(key string, data interface{}) (value interface{}) {
    if key == "" {
        return data
    }

    keys := strings.Split(key, ".");

    value = data;

    for _, k := range keys {
        // fmt.Println(data);

        switch t := value.(type) {
            case map[string]interface{}:
                value = t[k];

            default:
                // value doesn't exist b/c parent value isn't a map
                panic(fmt.Sprintf("err mer gerrrd, %s doesn't exist in %s", k, key));
        }
    }

    return value;
}

func getSortedKeys(m map[string]struct{}) ([]string) {
    sortedKeys := make([]string, len(m))
    i := 0
    for k, _ := range m {
        sortedKeys[i] = k
        i++
    }
    sort.Strings(sortedKeys)
    return sortedKeys
}

func (a *Analytics) Print() {
    fmt.Println("Key: " + a.key);
    fmt.Printf("Total Rows: %d\n", a.rows);
    fmt.Printf("Total Encounters: %d\n", a.encounters);
    if a.encounters > 0 {
        fmt.Printf("Average: %d\n", (a.sum / a.encounters));
    }
    fmt.Printf("Max: %d\n", a.max);
    fmt.Printf("Min: %d\n", a.min);
    fmt.Printf("Subkeys: %v\n", getSortedKeys(a.subkeys))
}
<commit_msg>print subkeys or stats, not both (should be mutually exclusive)<commit_after>package main;

import(
    "fmt"
    "sort"
    "strings"
)

type Analytics struct {
    key string
    rows int
    encounters int
    sum int
    max int
    min int
    subkeys map[string]struct{} // map used as a set
}

func NewAnalytics(key string) *Analytics {
    a := new(Analytics);

    a.key = key;
    a.rows = 0;
    a.encounters = 0;
    a.sum = 0;
    a.max = 0;
    a.min = 9999999999; // TODO: find a better way to represent max/min

    return a;
}

func (a *Analytics) NewRow(data map[string]interface{}) {
    a.rows += 1;

    watchedValue := nestedGet(a.key, data);

    switch casted := watchedValue.(type) {
        case int:
            a.numberEncountered(casted);
        case float64:
            a.numberEncountered(int(casted));
        case map[string]interface{}:
            a.mapEncountered(casted);
    }
}

func (a *Analytics) numberEncountered(encountered int) {
    a.encounters += 1;
    a.sum += encountered;

    if(a.max < encountered) {
        a.max = encountered;
    }

    if(a.min > encountered) {
        a.min = encountered;
    }
}

func (a *Analytics) mapEncountered(encountered map[string]interface{}) {
    a.encounters += 1;
    fmt.Sprintf("%q", encountered);
    // fmt.Println("not a leaf node!")

    if len(a.subkeys) == 0 {
        // make map and populate
        a.subkeys = make(map[string]struct{}, len(encountered))
        for k := range encountered {
            a.subkeys[k] = struct{}{}
        }
    } else {
        // iterate map, append to subkeys as necessary
        for k := range encountered {
            // check if subkeys contains k
            if _, inSet := a.subkeys[k]; !inSet {
                // fmt.Println("Found new subkey:", k)
                a.subkeys[k] = struct{}{}
            }
        }
    }
}

func nestedGet(key string, data interface{}) (value interface{}) {
    if key == "" {
        return data
    }

    keys := strings.Split(key, ".");

    value = data;

    for _, k := range keys {
        // fmt.Println(data);

        switch t := value.(type) {
            case map[string]interface{}:
                value = t[k];

            default:
                // value doesn't exist b/c parent value isn't a map
                panic(fmt.Sprintf("err mer gerrrd, %s doesn't exist in %s", k, key));
        }
    }

    return value;
}

func getSortedKeys(m map[string]struct{}) ([]string) {
    sortedKeys := make([]string, len(m))
    i := 0
    for k, _ := range m {
        sortedKeys[i] = k
        i++
    }
    sort.Strings(sortedKeys)
    return sortedKeys
}

func (a *Analytics) Print() {
    fmt.Println("Key: " + a.key);
    fmt.Printf("Total Rows: %d\n", a.rows);
    fmt.Printf("Total Encounters: %d\n", a.encounters);
    if len(a.subkeys) > 0 {
        fmt.Printf("Subkeys: %v\n", getSortedKeys(a.subkeys))
    } else {
        if a.encounters > 0 {
            fmt.Printf("Average: %d\n", (a.sum / a.encounters));
        }
        fmt.Printf("Max: %d\n", a.max);
        fmt.Printf("Min: %d\n", a.min);
    }
}
<|endoftext|>


<commit_before>package metrics

import (
	"github.com/loadimpact/k6/stats"
)

var (
	// Engine-emitted.
	VUs        = stats.New("vus", stats.Gauge)
	VUsMax     = stats.New("vus_max", stats.Gauge)
	Iterations = stats.New("iterations", stats.Gauge)
	Errors     = stats.New("errors", stats.Counter)

	// Runner-emitted.
	Checks = stats.New("checks", stats.Rate)

	// HTTP-related.
	HTTPReqs          = stats.New("http_reqs", stats.Counter)
	HTTPReqDuration   = stats.New("http_req_duration", stats.Trend, stats.Time)
	HTTPReqBlocked    = stats.New("http_req_blocked", stats.Trend, stats.Time)
	HTTPReqLookingUp  = stats.New("http_req_looking_up", stats.Trend, stats.Time)
	HTTPReqConnecting = stats.New("http_req_connecting", stats.Trend, stats.Time)
	HTTPReqSending    = stats.New("http_req_sending", stats.Trend, stats.Time)
	HTTPReqWaiting    = stats.New("http_req_waiting", stats.Trend, stats.Time)
	HTTPReqReceiving  = stats.New("http_req_receiving", stats.Trend, stats.Time)
)
<commit_msg>iterations is now a counter<commit_after>package metrics

import (
	"github.com/loadimpact/k6/stats"
)

var (
	// Engine-emitted.
	VUs        = stats.New("vus", stats.Gauge)
	VUsMax     = stats.New("vus_max", stats.Gauge)
	Iterations = stats.New("iterations", stats.Counter)
	Errors     = stats.New("errors", stats.Counter)

	// Runner-emitted.
	Checks = stats.New("checks", stats.Rate)

	// HTTP-related.
	HTTPReqs          = stats.New("http_reqs", stats.Counter)
	HTTPReqDuration   = stats.New("http_req_duration", stats.Trend, stats.Time)
	HTTPReqBlocked    = stats.New("http_req_blocked", stats.Trend, stats.Time)
	HTTPReqLookingUp  = stats.New("http_req_looking_up", stats.Trend, stats.Time)
	HTTPReqConnecting = stats.New("http_req_connecting", stats.Trend, stats.Time)
	HTTPReqSending    = stats.New("http_req_sending", stats.Trend, stats.Time)
	HTTPReqWaiting    = stats.New("http_req_waiting", stats.Trend, stats.Time)
	HTTPReqReceiving  = stats.New("http_req_receiving", stats.Trend, stats.Time)
)
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"github.com/coreos/go-etcd/etcd"
	"gopkg.in/BlueDragonX/simplelog.v1"
	"strconv"
	"strings"
)

// Return true if the err is an EtcdError and has the given error code.
func checkEtcdErrorCode(err error, code int) bool {
	etcdErr, ok := err.(*etcd.EtcdError)
	if ok && etcdErr.ErrorCode == code {
		return true
	}
	return false
}

type ServiceAnnouncer struct {
	client *etcd.Client
	prefix string
	ttl    uint64
	log    *simplelog.Logger
}

func newServiceAnnouncer(client *etcd.Client, prefix string, ttl uint64, log *simplelog.Logger) *ServiceAnnouncer {
	if prefix != "" {
		if prefix[0] != '/' {
			prefix = "/" + prefix
		}
		if prefix[len(prefix)-1] == '/' {
			prefix = prefix[:len(prefix)-1]
		}
	}
	ann := &ServiceAnnouncer{}
	ann.client = client
	ann.prefix = prefix
	ann.ttl = ttl
	ann.log = log
	return ann
}

// Create a new service announcer. Announce new services to the given etcd cluster.
func NewServiceAnnouncer(urls []string, prefix string, ttl uint64, log *simplelog.Logger) *ServiceAnnouncer {
	cleanUrls := make([]string, len(urls))
	for i, url := range urls {
		cleanUrls[i] = strings.TrimRight(url, "/")
	}
	return newServiceAnnouncer(etcd.NewClient(cleanUrls), prefix, ttl, log)
}

// Create a new service announcer. Announce new services to the given etcd cluster over TLS.
func NewTLSServiceAnnouncer(urls []string, cert, key, caCert, prefix string, ttl uint64, log *simplelog.Logger) (ann *ServiceAnnouncer, err error) {
	var client *etcd.Client
	if client, err = etcd.NewTLSClient(urls, cert, key, caCert); err == nil {
		ann = newServiceAnnouncer(client, prefix, ttl, log)
	}
	return
}

// Increment an index counter in etcd. Replace or create the key if it's not a valid int.
func (ann *ServiceAnnouncer) increment(key string) (value int, err error) {

	createKey := func() (value int, err error) {
		_, err = ann.client.Set(key, "1", 0)
		return 1, err
	}

	replaceKey := func() (value int, err error) {
		if _, err = ann.client.Delete(key, true); err == nil {
			value, err = createKey()
		}
		return
	}

	incrementKey := func(value int) (int, error) {
		newValue := value + 1
		newValueStr := fmt.Sprintf("%v", newValue)
		oldValueStr := fmt.Sprintf("%v", value)
		_, err := ann.client.CompareAndSwap(key, newValueStr, 0, oldValueStr, 0)
		return newValue, err
	}

	var response *etcd.Response

	for {
		if response, err = ann.client.Get(key, false, false); err != nil {
			value, err = createKey()
		} else if response.Node.Dir {
			// replace the directory with zero
			value, err = replaceKey()
		} else {
			if value, err = strconv.Atoi(response.Node.Value); err != nil {
				// parse the value and replace it with zero if we can't
				value, err = replaceKey()
			} else {
				// increment the value
				value, err = incrementKey(value)
				if err != nil {
					// convert value
					if etcdError, ok := err.(*etcd.EtcdError); ok {
						if etcdError.ErrorCode == 101 {
							continue
						}
					}
				}
			}
		}
		break
	}

	if err == nil {
		ann.log.Debug("increment %s to %d", key, value)
	} else {
		ann.log.Error("failed to increment %s: %s", key, err)
	}
	return
}

// Increment all indexes.
func (ann *ServiceAnnouncer) incrementIndexes(svc *Service) error {
	indexes := []string{
		fmt.Sprintf("%v/_index", ann.prefix),
		fmt.Sprintf("%v/%v/_index", ann.prefix, svc.Name),
	}

	for _, index := range indexes {
		if _, err := ann.increment(index); err != nil {
			return err
		}
	}
	return nil
}

// Return the path to the directory for a service.
func (ann *ServiceAnnouncer) getServicePath(svc *Service) string {
	return fmt.Sprintf("%v/%v/%v", ann.prefix, svc.Name, svc.ContainerId)
}

func (ann *ServiceAnnouncer) setValue(svc *Service, root, name, value string) (err error) {
	key := fmt.Sprintf("%v/%v", root, name)
	_, err = ann.client.Set(key, value, 0)
	if err == nil {
		ann.log.Debug("etcd set '%s' = '%s'", key, value)
	} else {
		ann.log.Error("etcd failed to set '%s' = '%s'", key, value)
	}
	return
}

func (ann *ServiceAnnouncer) addService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	if _, err = ann.client.SetDir(root, ann.ttl); err != nil {
		if checkEtcdErrorCode(err, 102) {
			_, err = ann.client.UpdateDir(root, ann.ttl)
		}
		if err != nil {
			return
		}
	}
	if err = ann.setValue(svc, root, "container-name", svc.ContainerName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "container-port", fmt.Sprintf("%d", svc.ContainerPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host-name", svc.HostName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host-port", fmt.Sprintf("%d", svc.HostPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "protocol", svc.Protocol); err != nil {
		return
	}
	if err = ann.incrementIndexes(svc); err != nil {
		return
	}
	return
}

func (ann *ServiceAnnouncer) heartbeatService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	_, err = ann.client.UpdateDir(root, ann.ttl)
	if checkEtcdErrorCode(err, 100) {
		err = ann.addService(svc)
	}
	return
}

func (ann *ServiceAnnouncer) cleanService(svc *Service) (err error) {
	svcKey := fmt.Sprintf("%v/%v", ann.prefix, svc.Name)
	indexKey := fmt.Sprintf("%v/index", svcKey)

	var response *etcd.Response
	if response, err = ann.client.Get(svcKey, false, false); err != nil {
		return
	}
	if !response.Node.Dir || len(response.Node.Nodes) > 1 {
		return
	}

	var indexNode *etcd.Node
	for _, node := range response.Node.Nodes {
		if node.Key == indexKey {
			indexNode = node
			break
		}
	}

	if indexNode == nil {
		return errors.New(fmt.Sprintf("%s not found", indexKey))
	}

	var index int
	if index, err = strconv.Atoi(indexNode.Value); err != nil {
		return
	}
	if _, err = ann.client.CompareAndDelete(indexKey, fmt.Sprintf("%d", index), 0); err != nil {
		return
	}
	_, err = ann.client.DeleteDir(svcKey)
	return
}

func (ann *ServiceAnnouncer) removeService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	key := fmt.Sprintf("%v/%v", ann.prefix, svc.Name)
	_, err = ann.client.Delete(root, true)
	if err == nil {
		err = ann.incrementIndexes(svc)
		if err == nil {
			err = ann.cleanService(svc)
		}
	}
	if err == nil {
		ann.log.Debug("etcd delete '%s'", key)
	} else {
		ann.log.Debug("etcd delete '%s' failed: %s", key, err)
	}
	return
}

func (ann *ServiceAnnouncer) Announce(event ServiceEvent) (err error) {
	switch event.State {
	case Add:
		err = ann.addService(event.Service)
	case Heartbeat:
		err = ann.heartbeatService(event.Service)
	case Remove:
		err = ann.removeService(event.Service)
	}
	return
}
<commit_msg>Use our go-etcd fork.<commit_after>package main

import (
	"errors"
	"fmt"
	"github.com/BlueDragonX/go-etcd/etcd"
	"gopkg.in/BlueDragonX/simplelog.v1"
	"strconv"
	"strings"
)

// Return true if the err is an EtcdError and has the given error code.
func checkEtcdErrorCode(err error, code int) bool {
	etcdErr, ok := err.(*etcd.EtcdError)
	if ok && etcdErr.ErrorCode == code {
		return true
	}
	return false
}

type ServiceAnnouncer struct {
	client *etcd.Client
	prefix string
	ttl    uint64
	log    *simplelog.Logger
}

func newServiceAnnouncer(client *etcd.Client, prefix string, ttl uint64, log *simplelog.Logger) *ServiceAnnouncer {
	if prefix != "" {
		if prefix[0] != '/' {
			prefix = "/" + prefix
		}
		if prefix[len(prefix)-1] == '/' {
			prefix = prefix[:len(prefix)-1]
		}
	}
	ann := &ServiceAnnouncer{}
	ann.client = client
	ann.prefix = prefix
	ann.ttl = ttl
	ann.log = log
	return ann
}

// Create a new service announcer. Announce new services to the given etcd cluster.
func NewServiceAnnouncer(urls []string, prefix string, ttl uint64, log *simplelog.Logger) *ServiceAnnouncer {
	cleanUrls := make([]string, len(urls))
	for i, url := range urls {
		cleanUrls[i] = strings.TrimRight(url, "/")
	}
	return newServiceAnnouncer(etcd.NewClient(cleanUrls), prefix, ttl, log)
}

// Create a new service announcer. Announce new services to the given etcd cluster over TLS.
func NewTLSServiceAnnouncer(urls []string, cert, key, caCert, prefix string, ttl uint64, log *simplelog.Logger) (ann *ServiceAnnouncer, err error) {
	var client *etcd.Client
	if client, err = etcd.NewTLSClient(urls, cert, key, caCert); err == nil {
		ann = newServiceAnnouncer(client, prefix, ttl, log)
	}
	return
}

// Increment an index counter in etcd. Replace or create the key if it's not a valid int.
func (ann *ServiceAnnouncer) increment(key string) (value int, err error) {

	createKey := func() (value int, err error) {
		_, err = ann.client.Set(key, "1", 0)
		return 1, err
	}

	replaceKey := func() (value int, err error) {
		if _, err = ann.client.Delete(key, true); err == nil {
			value, err = createKey()
		}
		return
	}

	incrementKey := func(value int) (int, error) {
		newValue := value + 1
		newValueStr := fmt.Sprintf("%v", newValue)
		oldValueStr := fmt.Sprintf("%v", value)
		_, err := ann.client.CompareAndSwap(key, newValueStr, 0, oldValueStr, 0)
		return newValue, err
	}

	var response *etcd.Response

	for {
		if response, err = ann.client.Get(key, false, false); err != nil {
			value, err = createKey()
		} else if response.Node.Dir {
			// replace the directory with zero
			value, err = replaceKey()
		} else {
			if value, err = strconv.Atoi(response.Node.Value); err != nil {
				// parse the value and replace it with zero if we can't
				value, err = replaceKey()
			} else {
				// increment the value
				value, err = incrementKey(value)
				if err != nil {
					// convert value
					if etcdError, ok := err.(*etcd.EtcdError); ok {
						if etcdError.ErrorCode == 101 {
							continue
						}
					}
				}
			}
		}
		break
	}

	if err == nil {
		ann.log.Debug("increment %s to %d", key, value)
	} else {
		ann.log.Error("failed to increment %s: %s", key, err)
	}
	return
}

// Increment all indexes.
func (ann *ServiceAnnouncer) incrementIndexes(svc *Service) error {
	indexes := []string{
		fmt.Sprintf("%v/_index", ann.prefix),
		fmt.Sprintf("%v/%v/_index", ann.prefix, svc.Name),
	}

	for _, index := range indexes {
		if _, err := ann.increment(index); err != nil {
			return err
		}
	}
	return nil
}

// Return the path to the directory for a service.
func (ann *ServiceAnnouncer) getServicePath(svc *Service) string {
	return fmt.Sprintf("%v/%v/%v", ann.prefix, svc.Name, svc.ContainerId)
}

func (ann *ServiceAnnouncer) setValue(svc *Service, root, name, value string) (err error) {
	key := fmt.Sprintf("%v/%v", root, name)
	_, err = ann.client.Set(key, value, 0)
	if err == nil {
		ann.log.Debug("etcd set '%s' = '%s'", key, value)
	} else {
		ann.log.Error("etcd failed to set '%s' = '%s'", key, value)
	}
	return
}

func (ann *ServiceAnnouncer) addService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	if _, err = ann.client.SetDir(root, ann.ttl); err != nil {
		if checkEtcdErrorCode(err, 102) {
			_, err = ann.client.UpdateDir(root, ann.ttl)
		}
		if err != nil {
			return
		}
	}
	if err = ann.setValue(svc, root, "container-name", svc.ContainerName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "container-port", fmt.Sprintf("%d", svc.ContainerPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host-name", svc.HostName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host-port", fmt.Sprintf("%d", svc.HostPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "protocol", svc.Protocol); err != nil {
		return
	}
	if err = ann.incrementIndexes(svc); err != nil {
		return
	}
	return
}

func (ann *ServiceAnnouncer) heartbeatService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	_, err = ann.client.UpdateDir(root, ann.ttl)
	if checkEtcdErrorCode(err, 100) {
		err = ann.addService(svc)
	}
	return
}

func (ann *ServiceAnnouncer) cleanService(svc *Service) (err error) {
	svcKey := fmt.Sprintf("%v/%v", ann.prefix, svc.Name)
	indexKey := fmt.Sprintf("%v/index", svcKey)

	var response *etcd.Response
	if response, err = ann.client.Get(svcKey, false, false); err != nil {
		return
	}
	if !response.Node.Dir || len(response.Node.Nodes) > 1 {
		return
	}

	var indexNode *etcd.Node
	for _, node := range response.Node.Nodes {
		if node.Key == indexKey {
			indexNode = node
			break
		}
	}

	if indexNode == nil {
		return errors.New(fmt.Sprintf("%s not found", indexKey))
	}

	var index int
	if index, err = strconv.Atoi(indexNode.Value); err != nil {
		return
	}
	if _, err = ann.client.CompareAndDelete(indexKey, fmt.Sprintf("%d", index), 0); err != nil {
		return
	}
	_, err = ann.client.DeleteDir(svcKey)
	return
}

func (ann *ServiceAnnouncer) removeService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	key := fmt.Sprintf("%v/%v", ann.prefix, svc.Name)
	_, err = ann.client.Delete(root, true)
	if err == nil {
		err = ann.incrementIndexes(svc)
		if err == nil {
			err = ann.cleanService(svc)
		}
	}
	if err == nil {
		ann.log.Debug("etcd delete '%s'", key)
	} else {
		ann.log.Debug("etcd delete '%s' failed: %s", key, err)
	}
	return
}

func (ann *ServiceAnnouncer) Announce(event ServiceEvent) (err error) {
	switch event.State {
	case Add:
		err = ann.addService(event.Service)
	case Heartbeat:
		err = ann.heartbeatService(event.Service)
	case Remove:
		err = ann.removeService(event.Service)
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/coreos/go-etcd/etcd"
)

// Return true if the err is an EtcdError and has the given error code.
func checkEtcdErrorCode(err error, code int) bool {
	etcdErr, ok := err.(*etcd.EtcdError)
	if ok && etcdErr.ErrorCode == code {
		return true
	}
	return false
}

type ServiceAnnouncer struct {
	client *etcd.Client
	prefix string
	ttl    uint64
}

func newServiceAnnouncer(client *etcd.Client, prefix string, ttl uint64) *ServiceAnnouncer {
	if prefix != "" {
		if prefix[0] != '/' {
			prefix = "/" + prefix
		}
		if prefix[len(prefix)-1] == '/' {
			prefix = prefix[:len(prefix)-1]
		}
	}
	ann := &ServiceAnnouncer{}
	ann.client = client
	ann.prefix = prefix
	ann.ttl = ttl
	return ann
}

// Create a new service announcer. Announce new services to the given etcd cluster.
func NewServiceAnnouncer(urls []string, prefix string, ttl uint64) *ServiceAnnouncer {
	return newServiceAnnouncer(etcd.NewClient(urls), prefix, ttl)
}

// Create a new service announcer. Announce new services to the given etcd cluster over TLS.
func NewTLSServiceAnnouncer(urls []string, cert, key, caCert, prefix string, ttl uint64) (ann *ServiceAnnouncer, err error) {
	var client *etcd.Client
	if client, err = etcd.NewTLSClient(urls, cert, key, caCert); err == nil {
		ann = newServiceAnnouncer(client, prefix, ttl)
	}
	return
}

// Return the path to the directory for a service.
func (ann *ServiceAnnouncer) getServicePath(svc *Service) string {
	return fmt.Sprintf("%v/%v/%v", ann.prefix, svc.Name, svc.ContainerId)
}

func (ann *ServiceAnnouncer) setValue(svc *Service, root, name, value string) (err error) {
	_, err = ann.client.Set(fmt.Sprintf("%v/%v", root, name), value, 0)
	return
}

func (ann *ServiceAnnouncer) addService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	if _, err = ann.client.SetDir(root, ann.ttl); err != nil {
		if checkEtcdErrorCode(err, 102) {
			_, err = ann.client.UpdateDir(root, ann.ttl)
		}
		if err != nil {
			return
		}
	}
	if err = ann.setValue(svc, root, "container_name", svc.ContainerName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "container_port", fmt.Sprintf("%d", svc.ContainerPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host_name", svc.HostName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host_port", fmt.Sprintf("%d", svc.HostPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "protocol", svc.Protocol); err != nil {
		return
	}
	return
}

func (ann *ServiceAnnouncer) heartbeatService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	_, err = ann.client.UpdateDir(root, ann.ttl)
	if checkEtcdErrorCode(err, 100) {
		err = ann.addService(svc)
	}
	return
}

func (ann *ServiceAnnouncer) removeService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	_, err = ann.client.Delete(root, true)
	ann.client.DeleteDir(fmt.Sprintf("%v/%v", ann.prefix, svc.Name))
	return
}

func (ann *ServiceAnnouncer) Announce(event ServiceEvent) (err error) {
	switch event.State {
	case Add:
		err = ann.addService(event.Service)
	case Heartbeat:
		err = ann.heartbeatService(event.Service)
	case Remove:
		err = ann.removeService(event.Service)
	}
	return
}
<commit_msg>Use dashes instead of underscores in key names.<commit_after>package main

import (
	"fmt"
	"github.com/coreos/go-etcd/etcd"
)

// Return true if the err is an EtcdError and has the given error code.
func checkEtcdErrorCode(err error, code int) bool {
	etcdErr, ok := err.(*etcd.EtcdError)
	if ok && etcdErr.ErrorCode == code {
		return true
	}
	return false
}

type ServiceAnnouncer struct {
	client *etcd.Client
	prefix string
	ttl    uint64
}

func newServiceAnnouncer(client *etcd.Client, prefix string, ttl uint64) *ServiceAnnouncer {
	if prefix != "" {
		if prefix[0] != '/' {
			prefix = "/" + prefix
		}
		if prefix[len(prefix)-1] == '/' {
			prefix = prefix[:len(prefix)-1]
		}
	}
	ann := &ServiceAnnouncer{}
	ann.client = client
	ann.prefix = prefix
	ann.ttl = ttl
	return ann
}

// Create a new service announcer. Announce new services to the given etcd cluster.
func NewServiceAnnouncer(urls []string, prefix string, ttl uint64) *ServiceAnnouncer {
	return newServiceAnnouncer(etcd.NewClient(urls), prefix, ttl)
}

// Create a new service announcer. Announce new services to the given etcd cluster over TLS.
func NewTLSServiceAnnouncer(urls []string, cert, key, caCert, prefix string, ttl uint64) (ann *ServiceAnnouncer, err error) {
	var client *etcd.Client
	if client, err = etcd.NewTLSClient(urls, cert, key, caCert); err == nil {
		ann = newServiceAnnouncer(client, prefix, ttl)
	}
	return
}

// Return the path to the directory for a service.
func (ann *ServiceAnnouncer) getServicePath(svc *Service) string {
	return fmt.Sprintf("%v/%v/%v", ann.prefix, svc.Name, svc.ContainerId)
}

func (ann *ServiceAnnouncer) setValue(svc *Service, root, name, value string) (err error) {
	_, err = ann.client.Set(fmt.Sprintf("%v/%v", root, name), value, 0)
	return
}

func (ann *ServiceAnnouncer) addService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	if _, err = ann.client.SetDir(root, ann.ttl); err != nil {
		if checkEtcdErrorCode(err, 102) {
			_, err = ann.client.UpdateDir(root, ann.ttl)
		}
		if err != nil {
			return
		}
	}
	if err = ann.setValue(svc, root, "container-name", svc.ContainerName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "container-port", fmt.Sprintf("%d", svc.ContainerPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host-name", svc.HostName); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "host-port", fmt.Sprintf("%d", svc.HostPort)); err != nil {
		return
	}
	if err = ann.setValue(svc, root, "protocol", svc.Protocol); err != nil {
		return
	}
	return
}

func (ann *ServiceAnnouncer) heartbeatService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	_, err = ann.client.UpdateDir(root, ann.ttl)
	if checkEtcdErrorCode(err, 100) {
		err = ann.addService(svc)
	}
	return
}

func (ann *ServiceAnnouncer) removeService(svc *Service) (err error) {
	root := ann.getServicePath(svc)
	_, err = ann.client.Delete(root, true)
	ann.client.DeleteDir(fmt.Sprintf("%v/%v", ann.prefix, svc.Name))
	return
}

func (ann *ServiceAnnouncer) Announce(event ServiceEvent) (err error) {
	switch event.State {
	case Add:
		err = ann.addService(event.Service)
	case Heartbeat:
		err = ann.heartbeatService(event.Service)
	case Remove:
		err = ann.removeService(event.Service)
	}
	return
}
<|endoftext|>


<commit_before>package rpc

import (
	"fmt"
	"io"
	"launchpad.net/juju-core/log"
	"reflect"
	"sync"
)

// A ServerCodec implements reading of RPC requests and writing of RPC
// responses for the server side of an RPC session.  The server calls
// ReadRequestHeader and ReadRequestBody in pairs to read requests from
// the connection, and it calls WriteResponse to write a response back.
// The params argument to ReadRequestBody will always be of struct type.
// The result argument to WriteResponse will always be a non-nil pointer to a struct.
type ServerCodec interface {
	ReadRequestHeader(req *Request) error
	ReadRequestBody(params interface{}) error
	WriteResponse(resp *Response, result interface{}) error
}

// Request is a header written before every RPC call.
type Request struct {
	// RequestId holds the sequence number of the request.
	RequestId uint64

	// Type holds the type of object to act on.
	Type string

	// Id holds the id of the object to act on.
	Id string

	// Request holds the action to invoke on the remote object.
	Request string
}

// Response is a header written before every RPC return.
type Response struct {
	// RequestId echoes that of the request.
	RequestId uint64

	// Error holds the error, if any.
	Error string

	// ErrorCode holds the code of the error, if any.
	ErrorCode string
}

// codecServer represents an active server instance.
type codecServer struct {
	*Server
	codec ServerCodec

	// pending represents the currently pending requests.
	pending sync.WaitGroup

	// root holds the root value being served.
	root reflect.Value

	// sending guards the write side of the codec.
	sending sync.Mutex
}

// ErrorCoder represents an any error that has an associated
// error code. An error code is a short string that describes the
// class of error.
type ErrorCoder interface {
	ErrorCode() string
}

// Killer represents a type that can be asked to
// abort any outstanding requests. The Kill
// method should return immediately.
type Killer interface {
	Kill()
}

// ServeCodec runs the server on a single connection.  ServeCodec
// blocks, serving the connection until the client hangs up.  The caller
// typically invokes ServeCodec in a go statement.  The given
// root value, which must be the same type as that passed to
// NewServer, is used to invoke the RPC requests. If rootValue
// nil, the original root value passed to NewServer will
// be used instead.
//
// ServeCodec stops serving requests when it receives an error
// reading a request. Before returning, if rootValue implements
// the Killer interface, its Kill method will be called.
// ServeCodec will then return only when all its outstanding calls have
// completed.
func (srv *Server) ServeCodec(codec ServerCodec, root interface{}) error {
	csrv := &codecServer{
		Server: srv,
		codec:  codec,
		root:   reflect.ValueOf(root),
	}
	if csrv.root.Type() != srv.root.Type() {
		panic(fmt.Errorf("rpc: unexpected type of root value; got %s, want %s", csrv.root.Type(), srv.root.Type()))
	}
	defer csrv.pending.Wait()
	err := csrv.serve()
	if killer, ok := root.(Killer); ok {
		killer.Kill()
	}
	return err
}

func (csrv *codecServer) serve() error {
	var req Request
	for {
		req = Request{}
		err := csrv.codec.ReadRequestHeader(&req)
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		o, a, err := csrv.findRequest(&req)
		if err != nil {
			_ = csrv.codec.ReadRequestBody(&struct{}{})
			resp := &Response{
				RequestId: req.RequestId,
			}
			csrv.setError(resp, err)
			if err := csrv.codec.WriteResponse(resp, struct{}{}); err != nil {
				return err
			}
			continue
		}
		var argp interface{}
		var arg reflect.Value
		if a.arg != nil {
			v := reflect.New(a.arg)
			arg = v.Elem()
			argp = v.Interface()
		} else {
			argp = &struct{}{}
		}
		if err := csrv.codec.ReadRequestBody(argp); err != nil {
			return fmt.Errorf("error reading request body: %v", err)
		}
		csrv.pending.Add(1)
		go csrv.runRequest(req.RequestId, req.Id, o, a, arg)
	}
	panic("unreachable")
}

func (csrv *codecServer) findRequest(req *Request) (*obtainer, *action, error) {
	o := csrv.obtain[req.Type]
	if o == nil {
		return nil, nil, fmt.Errorf("unknown object type %q", req.Type)
	}
	a := csrv.action[o.ret][req.Request]
	if a == nil {
		return nil, nil, fmt.Errorf("no such request %q on %s", req.Request, req.Type)
	}
	return o, a, nil
}

func (csrv *codecServer) setError(resp *Response, err error) {
	err = csrv.transformErrors(err)
	resp.Error = err.Error()
	if err, ok := err.(ErrorCoder); ok {
		resp.ErrorCode = err.ErrorCode()
	} else {
		resp.ErrorCode = ""
	}
}

// runRequest runs the given request and sends the reply.
func (csrv *codecServer) runRequest(reqId uint64, objId string, o *obtainer, a *action, arg reflect.Value) {
	defer csrv.pending.Done()
	rv, err := csrv.runRequest0(reqId, objId, o, a, arg)
	csrv.sending.Lock()
	defer csrv.sending.Unlock()
	var rvi interface{}
	resp := &Response{
		RequestId: reqId,
	}
	if err != nil {
		csrv.setError(resp, err)
		rvi = struct{}{}
	} else if rv.IsValid() {
		rvi = rv.Interface()
	} else {
		rvi = struct{}{}
	}
	if err := csrv.codec.WriteResponse(resp, rvi); err != nil {
		log.Errorf("rpc: error writing response %#v: %v", rvi, err)
	}
}

func (csrv *codecServer) runRequest0(reqId uint64, objId string, o *obtainer, a *action, arg reflect.Value) (reflect.Value, error) {
	obj, err := o.call(csrv.root, objId)
	if err != nil {
		return reflect.Value{}, err
	}
	return a.call(obj, arg)
}
<commit_msg>rpc: don't abort on error reading body<commit_after>package rpc

import (
	"fmt"
	"io"
	"launchpad.net/juju-core/log"
	"reflect"
	"sync"
)

// A ServerCodec implements reading of RPC requests and writing of RPC
// responses for the server side of an RPC session.  The server calls
// ReadRequestHeader and ReadRequestBody in pairs to read requests from
// the connection, and it calls WriteResponse to write a response back.
// The params argument to ReadRequestBody will always be of struct type.
// The result argument to WriteResponse will always be a non-nil pointer to a struct.
type ServerCodec interface {
	ReadRequestHeader(req *Request) error
	ReadRequestBody(params interface{}) error
	WriteResponse(resp *Response, result interface{}) error
}

// Request is a header written before every RPC call.
type Request struct {
	// RequestId holds the sequence number of the request.
	RequestId uint64

	// Type holds the type of object to act on.
	Type string

	// Id holds the id of the object to act on.
	Id string

	// Request holds the action to invoke on the remote object.
	Request string
}

// Response is a header written before every RPC return.
type Response struct {
	// RequestId echoes that of the request.
	RequestId uint64

	// Error holds the error, if any.
	Error string

	// ErrorCode holds the code of the error, if any.
	ErrorCode string
}

// codecServer represents an active server instance.
type codecServer struct {
	*Server
	codec ServerCodec

	// pending represents the currently pending requests.
	pending sync.WaitGroup

	// root holds the root value being served.
	root reflect.Value

	// sending guards the write side of the codec.
	sending sync.Mutex
}

// ErrorCoder represents an any error that has an associated
// error code. An error code is a short string that describes the
// class of error.
type ErrorCoder interface {
	ErrorCode() string
}

// Killer represents a type that can be asked to
// abort any outstanding requests. The Kill
// method should return immediately.
type Killer interface {
	Kill()
}

// ServeCodec runs the server on a single connection.  ServeCodec
// blocks, serving the connection until the client hangs up.  The caller
// typically invokes ServeCodec in a go statement.  The given
// root value, which must be the same type as that passed to
// NewServer, is used to invoke the RPC requests. If rootValue
// nil, the original root value passed to NewServer will
// be used instead.
//
// ServeCodec stops serving requests when it receives an error
// reading a request. Before returning, if rootValue implements
// the Killer interface, its Kill method will be called.
// ServeCodec will then return only when all its outstanding calls have
// completed.
func (srv *Server) ServeCodec(codec ServerCodec, root interface{}) error {
	csrv := &codecServer{
		Server: srv,
		codec:  codec,
		root:   reflect.ValueOf(root),
	}
	if csrv.root.Type() != srv.root.Type() {
		panic(fmt.Errorf("rpc: unexpected type of root value; got %s, want %s", csrv.root.Type(), srv.root.Type()))
	}
	defer csrv.pending.Wait()
	err := csrv.serve()
	if killer, ok := root.(Killer); ok {
		killer.Kill()
	}
	return err
}

func (csrv *codecServer) serve() error {
	var req Request
	for {
		req = Request{}
		err := csrv.codec.ReadRequestHeader(&req)
		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
		o, a, err := csrv.findRequest(&req)
		if err != nil {
			_ = csrv.codec.ReadRequestBody(&struct{}{})
			resp := &Response{
				RequestId: req.RequestId,
			}
			csrv.setError(resp, err)
			if err := csrv.codec.WriteResponse(resp, struct{}{}); err != nil {
				return err
			}
			continue
		}
		var argp interface{}
		var arg reflect.Value
		if a.arg != nil {
			v := reflect.New(a.arg)
			arg = v.Elem()
			argp = v.Interface()
		} else {
			argp = &struct{}{}
		}
		if err := csrv.codec.ReadRequestBody(argp); err != nil {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				return nil
			}
			// An error reading the body often indicates bad
			// request parameters rather than an issue with the
			// connection itself, so we reply with an error
			// rather than tearing down the connection
			// unless it's obviously a connection issue.
			resp := &Response{
				RequestId: req.RequestId,
			}
			csrv.setError(resp, err)
			if err := csrv.codec.WriteResponse(resp, struct{}{}); err != nil {
				return err
			}
			continue
		}
		csrv.pending.Add(1)
		go csrv.runRequest(req.RequestId, req.Id, o, a, arg)
	}
	panic("unreachable")
}

func (csrv *codecServer) findRequest(req *Request) (*obtainer, *action, error) {
	o := csrv.obtain[req.Type]
	if o == nil {
		return nil, nil, fmt.Errorf("unknown object type %q", req.Type)
	}
	a := csrv.action[o.ret][req.Request]
	if a == nil {
		return nil, nil, fmt.Errorf("no such request %q on %s", req.Request, req.Type)
	}
	return o, a, nil
}

func (csrv *codecServer) setError(resp *Response, err error) {
	err = csrv.transformErrors(err)
	resp.Error = err.Error()
	if err, ok := err.(ErrorCoder); ok {
		resp.ErrorCode = err.ErrorCode()
	} else {
		resp.ErrorCode = ""
	}
}

// runRequest runs the given request and sends the reply.
func (csrv *codecServer) runRequest(reqId uint64, objId string, o *obtainer, a *action, arg reflect.Value) {
	defer csrv.pending.Done()
	rv, err := csrv.runRequest0(reqId, objId, o, a, arg)
	csrv.sending.Lock()
	defer csrv.sending.Unlock()
	var rvi interface{}
	resp := &Response{
		RequestId: reqId,
	}
	if err != nil {
		csrv.setError(resp, err)
		rvi = struct{}{}
	} else if rv.IsValid() {
		rvi = rv.Interface()
	} else {
		rvi = struct{}{}
	}
	if err := csrv.codec.WriteResponse(resp, rvi); err != nil {
		log.Errorf("rpc: error writing response %#v: %v", rvi, err)
	}
}

func (csrv *codecServer) runRequest0(reqId uint64, objId string, o *obtainer, a *action, arg reflect.Value) (reflect.Value, error) {
	obj, err := o.call(csrv.root, objId)
	if err != nil {
		return reflect.Value{}, err
	}
	return a.call(obj, arg)
}
<|endoftext|>