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

package upgrader_test

import (
	"time"

	. "launchpad.net/gocheck"

	"launchpad.net/juju-core/errors"
	jujutesting "launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver/common"
	apitesting "launchpad.net/juju-core/state/apiserver/testing"
	"launchpad.net/juju-core/state/apiserver/upgrader"
	"launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/version"
)

type upgraderSuite struct {
	jujutesting.JujuConnSuite

	// These are raw State objects. Use them for setup and assertions, but
	// should never be touched by the API calls themselves
	rawMachine *state.Machine
	upgrader   *upgrader.UpgraderAPI
	resources  *common.Resources
	authorizer apitesting.FakeAuthorizer
}

var _ = Suite(&upgraderSuite{})

func (s *upgraderSuite) SetUpTest(c *C) {
	s.JujuConnSuite.SetUpTest(c)
	s.resources = common.NewResources()

	// Create a machine to work with
	var err error
	s.rawMachine, err = s.State.AddMachine("series", state.JobHostUnits)
	c.Assert(err, IsNil)
	err = s.rawMachine.SetPassword("test-password")
	c.Assert(err, IsNil)

	// The default auth is as the machine agent
	s.authorizer = apitesting.FakeAuthorizer{
		Tag:          s.rawMachine.Tag(),
		LoggedIn:     true,
		Manager:      false,
		MachineAgent: true,
		Client:       false,
	}
	s.upgrader, err = upgrader.NewUpgraderAPI(s.State, s.resources, s.authorizer)
	c.Assert(err, IsNil)
}

func (s *upgraderSuite) TearDownTest(c *C) {
	if s.resources != nil {
		s.resources.StopAll()
	}
	s.JujuConnSuite.TearDownTest(c)
}

func (s *upgraderSuite) TestWatchAPIVersionNothing(c *C) {
	// Not an error to watch nothing
	results, err := s.upgrader.WatchAPIVersion(params.Agents{})
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 0)
}

func (s *upgraderSuite) TestWatchAPIVersion(c *C) {
	args := params.Agents{
		Agents: []params.Agent{params.Agent{Tag: s.rawMachine.Tag()}},
	}
	results, err := s.upgrader.WatchAPIVersion(args)
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 1)
	c.Check(results.Results[0].EntityWatcherId, Not(Equals), "")
	resource := s.resources.Get(results.Results[0].EntityWatcherId)
	c.Check(resource, NotNil)

	// TODO: When this becomes a true EntityWatcher, use
	//       state/testing/watcher.go to properly test how this watcher is
	//       working
	defer func() {
		err := resource.Stop()
		c.Assert(err, IsNil)
	}()
	// Check that the watcher returns an initial event
	channel := resource.(*state.EnvironConfigWatcher).Changes()
	// Should use helpers from state/watcher_test.go when generalised
	select {
	case _, ok := <-channel:
		c.Assert(ok, Equals, true)
	case <-time.After(50 * time.Millisecond):
		c.Fatal("timeout waiting for entity watcher")
	}
}

func (s *upgraderSuite) TestUpgraderAPIRefusesNonAgent(c *C) {
	// We aren't even a machine agent
	anAuthorizer := s.authorizer
	anAuthorizer.MachineAgent = false
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, NotNil)
	c.Check(anUpgrader, IsNil)
	c.Assert(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestWatchAPIVersionRefusesWrongAgent(c *C) {
	// We are a machine agent, but not the one we are trying to track
	anAuthorizer := s.authorizer
	anAuthorizer.Tag = "machine-12354"
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, IsNil)
	args := params.Agents{
		Agents: []params.Agent{params.Agent{Tag: s.rawMachine.Tag()}},
	}
	results, err := anUpgrader.WatchAPIVersion(args)
	// It is not an error to make the request, but the specific item is rejected
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 1)
	c.Check(results.Results[0].EntityWatcherId, Equals, "")
	c.Assert(results.Results[0].Error, NotNil)
	err = *results.Results[0].Error
	c.Check(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestToolsNothing(c *C) {
	// Not an error to watch nothing
	results, err := s.upgrader.Tools(params.Agents{})
	c.Assert(err, IsNil)
	c.Check(results.Tools, HasLen, 0)
}

func (s *upgraderSuite) TestToolsRefusesWrongAgent(c *C) {
	anAuthorizer := s.authorizer
	anAuthorizer.Tag = "machine-12354"
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, IsNil)
	args := params.Agents{
		Agents: []params.Agent{params.Agent{Tag: s.rawMachine.Tag()}},
	}
	results, err := anUpgrader.Tools(args)
	// It is not an error to make the request, but the specific item is rejected
	c.Assert(err, IsNil)
	c.Check(results.Tools, HasLen, 1)
	toolResult := results.Tools[0]
	c.Check(toolResult.Tag, Equals, s.rawMachine.Tag())
	c.Assert(toolResult.Error, NotNil)
	err = *toolResult.Error
	c.Check(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestToolsForAgent(c *C) {
	cur := version.Current
	agent := params.Agent{
		Tag: s.rawMachine.Tag(),
	}

	// The machine must have its existing tools set before we query for the
	// next tools. This is so that we can grab Arch and Series without
	// having to pass it in again
	err := s.rawMachine.SetAgentTools(&state.Tools{
		URL:    "",
		Binary: version.Current,
	})
	c.Assert(err, IsNil)

	args := params.Agents{Agents: []params.Agent{agent}}
	results, err := s.upgrader.Tools(args)
	c.Assert(err, IsNil)
	c.Check(results.Tools, HasLen, 1)
	toolResult := results.Tools[0]
	c.Check(toolResult.Tag, Equals, s.rawMachine.Tag())
	c.Assert(toolResult.Error, IsNil)
	c.Check(toolResult.Major, Equals, cur.Major)
	c.Check(toolResult.Minor, Equals, cur.Minor)
	c.Check(toolResult.Patch, Equals, cur.Patch)
	c.Check(toolResult.Build, Equals, cur.Build)
	c.Check(toolResult.Arch, Equals, cur.Arch)
	c.Check(toolResult.Series, Equals, cur.Series)
	c.Check(toolResult.URL, Not(Equals), "")
}

func (s *upgraderSuite) TestSetToolsNothing(c *C) {
	// Not an error to watch nothing
	results, err := s.upgrader.SetTools(params.SetAgentTools{})
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 0)
}

func (s *upgraderSuite) TestSetToolsRefusesWrongAgent(c *C) {
	anAuthorizer := s.authorizer
	anAuthorizer.Tag = "machine-12354"
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, IsNil)
	cur := version.Current
	tools := params.AgentTools{
		Tag:    s.rawMachine.Tag(),
		Arch:   cur.Arch,
		Series: cur.Series,
		Major:  cur.Major,
		Minor:  cur.Minor,
		Patch:  cur.Patch,
		Build:  cur.Build,
		URL:    "",
	}
	args := params.SetAgentTools{AgentTools: []params.AgentTools{tools}}
	results, err := anUpgrader.SetTools(args)
	c.Assert(results.Results, HasLen, 1)
	c.Assert(results.Results[0].Tag, Equals, s.rawMachine.Tag())
	c.Assert(results.Results[0].Error, NotNil)
	err = *results.Results[0].Error
	c.Assert(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestSetTools(c *C) {
	cur := version.Current
	_, err := s.rawMachine.AgentTools()
	c.Assert(err, checkers.Satisfies, errors.IsNotFoundError)
	tools := params.AgentTools{
		Tag:    s.rawMachine.Tag(),
		Arch:   cur.Arch,
		Series: cur.Series,
		Major:  cur.Major,
		Minor:  cur.Minor,
		Patch:  cur.Patch,
		Build:  cur.Build,
		URL:    "",
	}
	args := params.SetAgentTools{AgentTools: []params.AgentTools{tools}}
	results, err := s.upgrader.SetTools(args)
	c.Assert(err, IsNil)
	c.Assert(results.Results, HasLen, 1)
	c.Assert(results.Results[0].Tag, Equals, s.rawMachine.Tag())
	c.Assert(results.Results[0].Error, IsNil)
	// Check that the new value actually got set, we must Refresh because
	// it was set on a different Machine object
	err = s.rawMachine.Refresh()
	c.Assert(err, IsNil)
	realTools, err := s.rawMachine.AgentTools()
	c.Assert(err, IsNil)
	c.Check(realTools.Arch, Equals, cur.Arch)
	c.Check(realTools.Series, Equals, cur.Series)
	c.Check(realTools.Major, Equals, cur.Major)
	c.Check(realTools.Minor, Equals, cur.Minor)
	c.Check(realTools.Patch, Equals, cur.Patch)
	c.Check(realTools.Build, Equals, cur.Build)
	c.Check(realTools.URL, Equals, "")
}
<commit_msg>rename in a test file<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package upgrader_test

import (
	"time"

	. "launchpad.net/gocheck"

	"launchpad.net/juju-core/errors"
	jujutesting "launchpad.net/juju-core/juju/testing"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/state/api/params"
	"launchpad.net/juju-core/state/apiserver/common"
	apitesting "launchpad.net/juju-core/state/apiserver/testing"
	"launchpad.net/juju-core/state/apiserver/upgrader"
	"launchpad.net/juju-core/testing/checkers"
	"launchpad.net/juju-core/version"
)

type upgraderSuite struct {
	jujutesting.JujuConnSuite

	// These are raw State objects. Use them for setup and assertions, but
	// should never be touched by the API calls themselves
	rawMachine *state.Machine
	upgrader   *upgrader.UpgraderAPI
	resources  *common.Resources
	authorizer apitesting.FakeAuthorizer
}

var _ = Suite(&upgraderSuite{})

func (s *upgraderSuite) SetUpTest(c *C) {
	s.JujuConnSuite.SetUpTest(c)
	s.resources = common.NewResources()

	// Create a machine to work with
	var err error
	s.rawMachine, err = s.State.AddMachine("series", state.JobHostUnits)
	c.Assert(err, IsNil)
	err = s.rawMachine.SetPassword("test-password")
	c.Assert(err, IsNil)

	// The default auth is as the machine agent
	s.authorizer = apitesting.FakeAuthorizer{
		Tag:          s.rawMachine.Tag(),
		LoggedIn:     true,
		Manager:      false,
		MachineAgent: true,
		Client:       false,
	}
	s.upgrader, err = upgrader.NewUpgraderAPI(s.State, s.resources, s.authorizer)
	c.Assert(err, IsNil)
}

func (s *upgraderSuite) TearDownTest(c *C) {
	if s.resources != nil {
		s.resources.StopAll()
	}
	s.JujuConnSuite.TearDownTest(c)
}

func (s *upgraderSuite) TestWatchAPIVersionNothing(c *C) {
	// Not an error to watch nothing
	results, err := s.upgrader.WatchAPIVersion(params.Agents{})
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 0)
}

func (s *upgraderSuite) TestWatchAPIVersion(c *C) {
	args := params.Agents{
		Agents: []params.Agent{params.Agent{Tag: s.rawMachine.Tag()}},
	}
	results, err := s.upgrader.WatchAPIVersion(args)
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 1)
	c.Check(results.Results[0].NotifyWatcherId, Not(Equals), "")
	resource := s.resources.Get(results.Results[0].NotifyWatcherId)
	c.Check(resource, NotNil)

	// TODO: When this becomes a true NotifyWatcher, use
	//       state/testing/watcher.go to properly test how this watcher is
	//       working
	defer func() {
		err := resource.Stop()
		c.Assert(err, IsNil)
	}()
	// Check that the watcher returns an initial event
	channel := resource.(*state.EnvironConfigWatcher).Changes()
	// Should use helpers from state/watcher_test.go when generalised
	select {
	case _, ok := <-channel:
		c.Assert(ok, Equals, true)
	case <-time.After(50 * time.Millisecond):
		c.Fatal("timeout waiting for entity watcher")
	}
}

func (s *upgraderSuite) TestUpgraderAPIRefusesNonAgent(c *C) {
	// We aren't even a machine agent
	anAuthorizer := s.authorizer
	anAuthorizer.MachineAgent = false
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, NotNil)
	c.Check(anUpgrader, IsNil)
	c.Assert(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestWatchAPIVersionRefusesWrongAgent(c *C) {
	// We are a machine agent, but not the one we are trying to track
	anAuthorizer := s.authorizer
	anAuthorizer.Tag = "machine-12354"
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, IsNil)
	args := params.Agents{
		Agents: []params.Agent{params.Agent{Tag: s.rawMachine.Tag()}},
	}
	results, err := anUpgrader.WatchAPIVersion(args)
	// It is not an error to make the request, but the specific item is rejected
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 1)
	c.Check(results.Results[0].NotifyWatcherId, Equals, "")
	c.Assert(results.Results[0].Error, NotNil)
	err = *results.Results[0].Error
	c.Check(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestToolsNothing(c *C) {
	// Not an error to watch nothing
	results, err := s.upgrader.Tools(params.Agents{})
	c.Assert(err, IsNil)
	c.Check(results.Tools, HasLen, 0)
}

func (s *upgraderSuite) TestToolsRefusesWrongAgent(c *C) {
	anAuthorizer := s.authorizer
	anAuthorizer.Tag = "machine-12354"
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, IsNil)
	args := params.Agents{
		Agents: []params.Agent{params.Agent{Tag: s.rawMachine.Tag()}},
	}
	results, err := anUpgrader.Tools(args)
	// It is not an error to make the request, but the specific item is rejected
	c.Assert(err, IsNil)
	c.Check(results.Tools, HasLen, 1)
	toolResult := results.Tools[0]
	c.Check(toolResult.Tag, Equals, s.rawMachine.Tag())
	c.Assert(toolResult.Error, NotNil)
	err = *toolResult.Error
	c.Check(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestToolsForAgent(c *C) {
	cur := version.Current
	agent := params.Agent{
		Tag: s.rawMachine.Tag(),
	}

	// The machine must have its existing tools set before we query for the
	// next tools. This is so that we can grab Arch and Series without
	// having to pass it in again
	err := s.rawMachine.SetAgentTools(&state.Tools{
		URL:    "",
		Binary: version.Current,
	})
	c.Assert(err, IsNil)

	args := params.Agents{Agents: []params.Agent{agent}}
	results, err := s.upgrader.Tools(args)
	c.Assert(err, IsNil)
	c.Check(results.Tools, HasLen, 1)
	toolResult := results.Tools[0]
	c.Check(toolResult.Tag, Equals, s.rawMachine.Tag())
	c.Assert(toolResult.Error, IsNil)
	c.Check(toolResult.Major, Equals, cur.Major)
	c.Check(toolResult.Minor, Equals, cur.Minor)
	c.Check(toolResult.Patch, Equals, cur.Patch)
	c.Check(toolResult.Build, Equals, cur.Build)
	c.Check(toolResult.Arch, Equals, cur.Arch)
	c.Check(toolResult.Series, Equals, cur.Series)
	c.Check(toolResult.URL, Not(Equals), "")
}

func (s *upgraderSuite) TestSetToolsNothing(c *C) {
	// Not an error to watch nothing
	results, err := s.upgrader.SetTools(params.SetAgentTools{})
	c.Assert(err, IsNil)
	c.Check(results.Results, HasLen, 0)
}

func (s *upgraderSuite) TestSetToolsRefusesWrongAgent(c *C) {
	anAuthorizer := s.authorizer
	anAuthorizer.Tag = "machine-12354"
	anUpgrader, err := upgrader.NewUpgraderAPI(s.State, s.resources, anAuthorizer)
	c.Check(err, IsNil)
	cur := version.Current
	tools := params.AgentTools{
		Tag:    s.rawMachine.Tag(),
		Arch:   cur.Arch,
		Series: cur.Series,
		Major:  cur.Major,
		Minor:  cur.Minor,
		Patch:  cur.Patch,
		Build:  cur.Build,
		URL:    "",
	}
	args := params.SetAgentTools{AgentTools: []params.AgentTools{tools}}
	results, err := anUpgrader.SetTools(args)
	c.Assert(results.Results, HasLen, 1)
	c.Assert(results.Results[0].Tag, Equals, s.rawMachine.Tag())
	c.Assert(results.Results[0].Error, NotNil)
	err = *results.Results[0].Error
	c.Assert(err, ErrorMatches, "permission denied")
}

func (s *upgraderSuite) TestSetTools(c *C) {
	cur := version.Current
	_, err := s.rawMachine.AgentTools()
	c.Assert(err, checkers.Satisfies, errors.IsNotFoundError)
	tools := params.AgentTools{
		Tag:    s.rawMachine.Tag(),
		Arch:   cur.Arch,
		Series: cur.Series,
		Major:  cur.Major,
		Minor:  cur.Minor,
		Patch:  cur.Patch,
		Build:  cur.Build,
		URL:    "",
	}
	args := params.SetAgentTools{AgentTools: []params.AgentTools{tools}}
	results, err := s.upgrader.SetTools(args)
	c.Assert(err, IsNil)
	c.Assert(results.Results, HasLen, 1)
	c.Assert(results.Results[0].Tag, Equals, s.rawMachine.Tag())
	c.Assert(results.Results[0].Error, IsNil)
	// Check that the new value actually got set, we must Refresh because
	// it was set on a different Machine object
	err = s.rawMachine.Refresh()
	c.Assert(err, IsNil)
	realTools, err := s.rawMachine.AgentTools()
	c.Assert(err, IsNil)
	c.Check(realTools.Arch, Equals, cur.Arch)
	c.Check(realTools.Series, Equals, cur.Series)
	c.Check(realTools.Major, Equals, cur.Major)
	c.Check(realTools.Minor, Equals, cur.Minor)
	c.Check(realTools.Patch, Equals, cur.Patch)
	c.Check(realTools.Build, Equals, cur.Build)
	c.Check(realTools.URL, Equals, "")
}
<|endoftext|>


<commit_before>package models

import (
	"github.com/jgraham909/bloggo/app"
	"github.com/robfig/revel"
	"labix.org/v2/mgo"
	"reflect"
	"strings"
)

var (
	Collections map[string]string
)

// Empty struct to embed in models that will provide application default funcs.
type Model struct{}

func Collection(m interface{}, s *mgo.Session) *mgo.Collection {
	typ := reflect.TypeOf(m)
	i := strings.LastIndex(typ.String(), ".") + 1
	n := typ.String()[i:len(typ.String())]

	var found bool
	var c string
	if c, found = revel.Config.String("bloggo.db.collection." + n); !found {
		c = n
	}
	return s.DB(app.DB).C(c)
}

// It is expected that each model will embed the type 'Model' and then extend
// or override the following functions to enforce corresponding business rules.
func (m *Model) CanBeCreatedBy(s *mgo.Session, u *User) bool {
	// Default nobody can create.
	return false
}

func (m *Model) CanBeReadBy(s *mgo.Session, u *User) bool {
	// Default everybody can read.
	return true
}

func (m *Model) CanBeDeletedBy(s *mgo.Session, u *User) bool {
	// Default nobody can delete.
	return false
}

func (m *Model) CanBeUpdatedBy(s *mgo.Session, u *User) bool {
	// Default nobody can update.
	return false
}
<commit_msg>More elegant way of getting Type's name<commit_after>package models

import (
	"github.com/jgraham909/bloggo/app"
	"github.com/robfig/revel"
	"labix.org/v2/mgo"
	"reflect"
)

var (
	Collections map[string]string
)

// Empty struct to embed in models that will provide application default funcs.
type Model struct{}

func Collection(m interface{}, s *mgo.Session) *mgo.Collection {
	typ := reflect.TypeOf(m).Elem()
	n := typ.Name()

	var found bool
	var c string
	if c, found = revel.Config.String("bloggo.db.collection." + n); !found {
		c = n
	}
	return s.DB(app.DB).C(c)
}

// It is expected that each model will embed the type 'Model' and then extend
// or override the following functions to enforce corresponding business rules.
func (m *Model) CanBeCreatedBy(s *mgo.Session, u *User) bool {
	// Default nobody can create.
	return false
}

func (m *Model) CanBeReadBy(s *mgo.Session, u *User) bool {
	// Default everybody can read.
	return true
}

func (m *Model) CanBeDeletedBy(s *mgo.Session, u *User) bool {
	// Default nobody can delete.
	return false
}

func (m *Model) CanBeUpdatedBy(s *mgo.Session, u *User) bool {
	// Default nobody can update.
	return false
}
<|endoftext|>


<commit_before>package peppy

import (
	"fmt"
	"strings"

	"github.com/jmoiron/sqlx"
	"github.com/valyala/fasthttp"
	"gopkg.in/thehowl/go-osuapi.v1"
	"zxq.co/ripple/rippleapi/common"
	"zxq.co/x/getrank"
)

// GetUserRecent retrieves an user's recent scores.
func GetUserRecent(c *fasthttp.RequestCtx, db *sqlx.DB) {
	getUserX(c, db, "ORDER BY scores.time DESC", common.InString(1, query(c, "limit"), 50, 10))
}

// GetUserBest retrieves an user's best scores.
func GetUserBest(c *fasthttp.RequestCtx, db *sqlx.DB) {
	var sb string
	if rankable(query(c, "m")) {
		sb = "scores.pp"
	} else {
		sb = "scores.score"
	}
	getUserX(c, db, "AND completed = '3' ORDER BY "+sb+" DESC", common.InString(1, query(c, "limit"), 100, 10))
}

func getUserX(c *fasthttp.RequestCtx, db *sqlx.DB, orderBy string, limit int) {
	whereClause, p := genUser(c, db)
	relaxWhere := genRelax(c)
	sqlQuery := fmt.Sprintf(
		`SELECT
			beatmaps.beatmap_id, scores.score, scores.max_combo,
			scores.300_count, scores.100_count, scores.50_count,
			scores.gekis_count, scores.katus_count, scores.misses_count,
			scores.full_combo, scores.mods, users.id, scores.time,
			scores.pp, scores.accuracy
		FROM scores
		LEFT JOIN beatmaps ON beatmaps.beatmap_md5 = scores.beatmap_md5
		LEFT JOIN users ON scores.userid = users.id
		WHERE %s
		AND scores.play_mode = ?
		AND users.is_public = 1
		(%s)
		AND %s
		LIMIT %d`, whereClause, relaxWhere, orderBy, limit,
	)
	scores := make([]osuapi.GUSScore, 0, limit)
	m := genmodei(query(c, "m"))
	rows, err := db.Query(sqlQuery, p, m)
	if err != nil {
		json(c, 200, defaultResponse)
		common.Err(c, err)
		return
	}
	for rows.Next() {
		var (
			curscore osuapi.GUSScore
			rawTime  common.UnixTimestamp
			acc      float64
			fc       bool
			mods     int
			bid      *int
		)
		err := rows.Scan(
			&bid, &curscore.Score.Score, &curscore.MaxCombo,
			&curscore.Count300, &curscore.Count100, &curscore.Count50,
			&curscore.CountGeki, &curscore.CountKatu, &curscore.CountMiss,
			&fc, &mods, &curscore.UserID, &rawTime,
			&curscore.PP, &acc,
		)
		if err != nil {
			json(c, 200, defaultResponse)
			common.Err(c, err)
			return
		}
		if bid == nil {
			curscore.BeatmapID = 0
		} else {
			curscore.BeatmapID = *bid
		}
		curscore.FullCombo = osuapi.OsuBool(fc)
		curscore.Mods = osuapi.Mods(mods)
		curscore.Date = osuapi.MySQLDate(rawTime)
		curscore.Rank = strings.ToUpper(getrank.GetRank(
			osuapi.Mode(m),
			curscore.Mods,
			acc,
			curscore.Count300,
			curscore.Count100,
			curscore.Count50,
			curscore.CountMiss,
		))
		scores = append(scores, curscore)
	}
	json(c, 200, scores)
}
<commit_msg>Fix get_user_x query error<commit_after>package peppy

import (
	"fmt"
	"strings"

	"github.com/jmoiron/sqlx"
	"github.com/valyala/fasthttp"
	"gopkg.in/thehowl/go-osuapi.v1"
	"zxq.co/ripple/rippleapi/common"
	"zxq.co/x/getrank"
)

// GetUserRecent retrieves an user's recent scores.
func GetUserRecent(c *fasthttp.RequestCtx, db *sqlx.DB) {
	getUserX(c, db, "ORDER BY scores.time DESC", common.InString(1, query(c, "limit"), 50, 10))
}

// GetUserBest retrieves an user's best scores.
func GetUserBest(c *fasthttp.RequestCtx, db *sqlx.DB) {
	var sb string
	if rankable(query(c, "m")) {
		sb = "scores.pp"
	} else {
		sb = "scores.score"
	}
	getUserX(c, db, "AND completed = '3' ORDER BY "+sb+" DESC", common.InString(1, query(c, "limit"), 100, 10))
}

func getUserX(c *fasthttp.RequestCtx, db *sqlx.DB, orderBy string, limit int) {
	whereClause, p := genUser(c, db)
	relaxWhere := genRelax(c)
	sqlQuery := fmt.Sprintf(
		`SELECT
			beatmaps.beatmap_id, scores.score, scores.max_combo,
			scores.300_count, scores.100_count, scores.50_count,
			scores.gekis_count, scores.katus_count, scores.misses_count,
			scores.full_combo, scores.mods, users.id, scores.time,
			scores.pp, scores.accuracy
		FROM scores
		LEFT JOIN beatmaps ON beatmaps.beatmap_md5 = scores.beatmap_md5
		LEFT JOIN users ON scores.userid = users.id
		WHERE %s
		AND scores.play_mode = ?
		AND users.is_public = 1
		AND
		(%s)
		%s
		LIMIT %d`, whereClause, relaxWhere, orderBy, limit,
	)
	fmt.Printf("%s", sqlQuery)
	scores := make([]osuapi.GUSScore, 0, limit)
	m := genmodei(query(c, "m"))
	rows, err := db.Query(sqlQuery, p, m)
	if err != nil {
		json(c, 200, defaultResponse)
		common.Err(c, err)
		return
	}
	for rows.Next() {
		var (
			curscore osuapi.GUSScore
			rawTime  common.UnixTimestamp
			acc      float64
			fc       bool
			mods     int
			bid      *int
		)
		err := rows.Scan(
			&bid, &curscore.Score.Score, &curscore.MaxCombo,
			&curscore.Count300, &curscore.Count100, &curscore.Count50,
			&curscore.CountGeki, &curscore.CountKatu, &curscore.CountMiss,
			&fc, &mods, &curscore.UserID, &rawTime,
			&curscore.PP, &acc,
		)
		if err != nil {
			json(c, 200, defaultResponse)
			common.Err(c, err)
			return
		}
		if bid == nil {
			curscore.BeatmapID = 0
		} else {
			curscore.BeatmapID = *bid
		}
		curscore.FullCombo = osuapi.OsuBool(fc)
		curscore.Mods = osuapi.Mods(mods)
		curscore.Date = osuapi.MySQLDate(rawTime)
		curscore.Rank = strings.ToUpper(getrank.GetRank(
			osuapi.Mode(m),
			curscore.Mods,
			acc,
			curscore.Count300,
			curscore.Count100,
			curscore.Count50,
			curscore.CountMiss,
		))
		scores = append(scores, curscore)
	}
	json(c, 200, scores)
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 elasticseach

import (
	"encoding/json"
	"errors"
	"strconv"

	elastigo "github.com/mattbaird/elastigo/lib"

	"github.com/redhat-cip/skydive/config"
	"github.com/redhat-cip/skydive/flow"
	"github.com/redhat-cip/skydive/logging"
	"github.com/redhat-cip/skydive/storage"
)

type FlatFlowEndpointStatistics struct {
	Type    flow.FlowEndpointType
	Value   string
	Packets uint64
	Bytes   uint64
}

type FlatFlowEndpointsStatistics struct {
	Type flow.FlowEndpointType
	AB   FlatFlowEndpointStatistics /* A->B */
	BA   FlatFlowEndpointStatistics /* B->A */
}

type FlatFlowStatistics struct {
	Start     int64
	Last      int64
	Endpoints []FlatFlowEndpointsStatistics
}

type FlatFlow struct {
	UUID       string
	LayersPath string
	/* Data Flow info */
	Statistics FlatFlowStatistics
	/* Topology info */
	ProbeGraphPath string
	IfSrcGraphPath string
	IfDstGraphPath string
}

func flow2FlatFlow(f *flow.Flow) FlatFlow {
	ff := FlatFlow{}

	ff.UUID = f.UUID
	ff.LayersPath = f.LayersPath
	ff.ProbeGraphPath = f.ProbeGraphPath
	ff.IfSrcGraphPath = f.IfSrcGraphPath
	ff.IfDstGraphPath = f.IfDstGraphPath

	fs := f.GetStatistics()
	ffs := FlatFlowStatistics{}
	ffs.Start = fs.Start
	ffs.Last = fs.Last
	for _, endp := range fs.Endpoints {
		ffes := FlatFlowEndpointsStatistics{}
		ffes.Type = endp.Type
		ffes.AB.Type = endp.Type
		ffes.AB.Value = endp.AB.Value
		ffes.AB.Packets = endp.AB.Packets
		ffes.AB.Bytes = endp.AB.Bytes
		ffes.BA.Type = endp.Type
		ffes.BA.Value = endp.BA.Value
		ffes.BA.Packets = endp.BA.Packets
		ffes.BA.Bytes = endp.BA.Bytes
		ffs.Endpoints = append(ffs.Endpoints, ffes)
	}
	ff.Statistics = ffs
	return ff
}

type ElasticSearchStorage struct {
	connection *elastigo.Conn
}

func (c *ElasticSearchStorage) StoreFlows(flows []*flow.Flow) error {
	/* TODO(safchain) bulk insert */
	for _, flow := range flows {
		_, err := c.connection.Index("skydive", "flow", flow.UUID, nil, flow2FlatFlow(flow))
		if err != nil {
			logging.GetLogger().Error("Error while indexing: %s", err.Error())
			continue
		}
	}

	return nil
}

func (c *ElasticSearchStorage) SearchFlows(filters storage.Filters) ([]*flow.Flow, error) {
	query := map[string]interface{}{
		"sort": map[string]interface{}{
			"Timestamp": map[string]string{
				"order": "desc",
			},
		},
		"from": 0,
		"size": 5,
	}
	if len(filters) > 0 {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"term": filters,
			},
			"sort": map[string]interface{}{
				"Timestamp": map[string]string{
					"order": "desc",
				},
			},
			"from": 0,
			"size": 5,
		}
	}

	q, err := json.Marshal(query)
	if err != nil {
		return nil, err
	}

	out, err := c.connection.Search("skydive", "flow", nil, string(q))
	if err != nil {
		return nil, err
	}

	flows := []*flow.Flow{}

	if out.Hits.Len() > 0 {
		for _, d := range out.Hits.Hits {
			f := new(flow.Flow)
			err := json.Unmarshal([]byte(*d.Source), f)
			if err != nil {
				return nil, err
			}

			flows = append(flows, f)
		}
	}

	return flows, nil
}

func (c *ElasticSearchStorage) initialize() error {
	req, err := c.connection.NewRequest("GET", "/skydive", "")
	if err != nil {
		return err
	}

	var response map[string]interface{}
	code, _, _ := req.Do(&response)
	if code == 200 {
		return nil
	}

	// template to remove the analyzer
	req, err = c.connection.NewRequest("PUT", "/skydive", "")
	if err != nil {
		return err
	}

	body := `{"mappings":{"flow":{"dynamic_templates":[{"notanalyzed":{"match":"*","mapping":{"type":"string","index":"not_analyzed"}}}]}}}`
	req.SetBodyString(body)

	code, _, err = req.Do(&response)
	if err != nil {
		return err
	}

	if code != 200 {
		return errors.New("Unable to create the skydive index: " + strconv.FormatInt(int64(code), 10))
	}

	return nil
}

var ErrBadConfig = errors.New("elasticseach : Config file is misconfigured, check elasticsearch key format")

func New() (*ElasticSearchStorage, error) {
	c := elastigo.NewConn()

	elasticonfig := config.GetConfig().Section("storage").Key("elasticsearch").Strings(":")
	if len(elasticonfig) != 2 {
		return nil, ErrBadConfig
	}
	c.Domain = elasticonfig[0]
	c.Port = elasticonfig[1]

	storage := &ElasticSearchStorage{connection: c}

	err := storage.initialize()
	if err != nil {
		return nil, err
	}

	return storage, nil
}
<commit_msg>[storage][elasticsearch] Introduce Bulk Indexer<commit_after>/*
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 elasticseach

import (
	"encoding/json"
	"errors"
	"strconv"

	elastigo "github.com/mattbaird/elastigo/lib"

	"github.com/redhat-cip/skydive/config"
	"github.com/redhat-cip/skydive/flow"
	"github.com/redhat-cip/skydive/logging"
	"github.com/redhat-cip/skydive/storage"
)

type FlatFlowEndpointStatistics struct {
	Type    flow.FlowEndpointType
	Value   string
	Packets uint64
	Bytes   uint64
}

type FlatFlowEndpointsStatistics struct {
	Type flow.FlowEndpointType
	AB   FlatFlowEndpointStatistics /* A->B */
	BA   FlatFlowEndpointStatistics /* B->A */
}

type FlatFlowStatistics struct {
	Start     int64
	Last      int64
	Endpoints []FlatFlowEndpointsStatistics
}

type FlatFlow struct {
	UUID       string
	LayersPath string
	/* Data Flow info */
	Statistics FlatFlowStatistics
	/* Topology info */
	ProbeGraphPath string
	IfSrcGraphPath string
	IfDstGraphPath string
}

func flow2FlatFlow(f *flow.Flow) FlatFlow {
	ff := FlatFlow{}

	ff.UUID = f.UUID
	ff.LayersPath = f.LayersPath
	ff.ProbeGraphPath = f.ProbeGraphPath
	ff.IfSrcGraphPath = f.IfSrcGraphPath
	ff.IfDstGraphPath = f.IfDstGraphPath

	fs := f.GetStatistics()
	ffs := FlatFlowStatistics{}
	ffs.Start = fs.Start
	ffs.Last = fs.Last
	for _, endp := range fs.Endpoints {
		ffes := FlatFlowEndpointsStatistics{}
		ffes.Type = endp.Type
		ffes.AB.Type = endp.Type
		ffes.AB.Value = endp.AB.Value
		ffes.AB.Packets = endp.AB.Packets
		ffes.AB.Bytes = endp.AB.Bytes
		ffes.BA.Type = endp.Type
		ffes.BA.Value = endp.BA.Value
		ffes.BA.Packets = endp.BA.Packets
		ffes.BA.Bytes = endp.BA.Bytes
		ffs.Endpoints = append(ffs.Endpoints, ffes)
	}
	ff.Statistics = ffs
	return ff
}

type ElasticSearchStorage struct {
	connection *elastigo.Conn
	indexer    *elastigo.BulkIndexer
}

func (c *ElasticSearchStorage) StoreFlows(flows []*flow.Flow) error {
	c.indexer.Start()
	defer c.indexer.Stop()

	for _, flow := range flows {
		err := c.indexer.Index("skydive", "flow", flow.UUID, "", "", nil, flow2FlatFlow(flow))
		if err != nil {
			logging.GetLogger().Error("Error while indexing: %s", err.Error())
			continue
		}
	}

	return nil
}

func (c *ElasticSearchStorage) SearchFlows(filters storage.Filters) ([]*flow.Flow, error) {
	query := map[string]interface{}{
		"sort": map[string]interface{}{
			"Timestamp": map[string]string{
				"order": "desc",
			},
		},
		"from": 0,
		"size": 5,
	}
	if len(filters) > 0 {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"term": filters,
			},
			"sort": map[string]interface{}{
				"Timestamp": map[string]string{
					"order": "desc",
				},
			},
			"from": 0,
			"size": 5,
		}
	}

	q, err := json.Marshal(query)
	if err != nil {
		return nil, err
	}

	out, err := c.connection.Search("skydive", "flow", nil, string(q))
	if err != nil {
		return nil, err
	}

	flows := []*flow.Flow{}

	if out.Hits.Len() > 0 {
		for _, d := range out.Hits.Hits {
			f := new(flow.Flow)
			err := json.Unmarshal([]byte(*d.Source), f)
			if err != nil {
				return nil, err
			}

			flows = append(flows, f)
		}
	}

	return flows, nil
}

func (c *ElasticSearchStorage) initialize() error {
	req, err := c.connection.NewRequest("GET", "/skydive", "")
	if err != nil {
		return err
	}

	var response map[string]interface{}
	code, _, _ := req.Do(&response)
	if code == 200 {
		return nil
	}

	// template to remove the analyzer
	req, err = c.connection.NewRequest("PUT", "/skydive", "")
	if err != nil {
		return err
	}

	body := `{"mappings":{"flow":{"dynamic_templates":[{"notanalyzed":{"match":"*","mapping":{"type":"string","index":"not_analyzed"}}}]}}}`
	req.SetBodyString(body)

	code, _, err = req.Do(&response)
	if err != nil {
		return err
	}

	if code != 200 {
		return errors.New("Unable to create the skydive index: " + strconv.FormatInt(int64(code), 10))
	}

	c.indexer = c.connection.NewBulkIndexerErrors(10, 60)

	return nil
}

var ErrBadConfig = errors.New("elasticseach : Config file is misconfigured, check elasticsearch key format")

func New() (*ElasticSearchStorage, error) {
	c := elastigo.NewConn()

	elasticonfig := config.GetConfig().Section("storage").Key("elasticsearch").Strings(":")
	if len(elasticonfig) != 2 {
		return nil, ErrBadConfig
	}
	c.Domain = elasticonfig[0]
	c.Port = elasticonfig[1]

	storage := &ElasticSearchStorage{connection: c}

	err := storage.initialize()
	if err != nil {
		return nil, err
	}

	return storage, nil
}
<|endoftext|>


<commit_before>package chart

import (
	"errors"
	"io"
	"math"

	"github.com/golang/freetype/truetype"
)

// BarChart is a chart that draws bars on a range.
type BarChart struct {
	Title      string
	TitleStyle Style

	Width  int
	Height int
	DPI    float64

	BarWidth int

	Background Style
	Canvas     Style

	XAxis Style
	YAxis YAxis

	BarSpacing int

	Font        *truetype.Font
	defaultFont *truetype.Font

	Bars     []Value
	Elements []Renderable
}

// GetDPI returns the dpi for the chart.
func (bc BarChart) GetDPI() float64 {
	if bc.DPI == 0 {
		return DefaultDPI
	}
	return bc.DPI
}

// GetFont returns the text font.
func (bc BarChart) GetFont() *truetype.Font {
	if bc.Font == nil {
		return bc.defaultFont
	}
	return bc.Font
}

// GetWidth returns the chart width or the default value.
func (bc BarChart) GetWidth() int {
	if bc.Width == 0 {
		return DefaultChartWidth
	}
	return bc.Width
}

// GetHeight returns the chart height or the default value.
func (bc BarChart) GetHeight() int {
	if bc.Height == 0 {
		return DefaultChartHeight
	}
	return bc.Height
}

// GetBarSpacing returns the spacing between bars.
func (bc BarChart) GetBarSpacing() int {
	if bc.BarSpacing == 0 {
		return DefaultBarSpacing
	}
	return bc.BarSpacing
}

// GetBarWidth returns the default bar width.
func (bc BarChart) GetBarWidth() int {
	if bc.BarWidth == 0 {
		return DefaultBarWidth
	}
	return bc.BarWidth
}

// Render renders the chart with the given renderer to the given io.Writer.
func (bc BarChart) Render(rp RendererProvider, w io.Writer) error {
	if len(bc.Bars) == 0 {
		return errors.New("Please provide at least one bar.")
	}

	r, err := rp(bc.GetWidth(), bc.GetHeight())
	if err != nil {
		return err
	}

	if bc.Font == nil {
		defaultFont, err := GetDefaultFont()
		if err != nil {
			return err
		}
		bc.defaultFont = defaultFont
	}
	r.SetDPI(bc.GetDPI())

	bc.drawBackground(r)

	var canvasBox Box
	var yt []Tick
	var yr Range
	var yf ValueFormatter

	canvasBox = bc.getDefaultCanvasBox()
	yr = bc.getRanges()
	yr = bc.setRangeDomains(canvasBox, yr)
	yf = bc.getValueFormatters()

	if bc.hasAxes() {
		yt = bc.getAxesTicks(r, yr, yf)
		canvasBox = bc.getAdjustedCanvasBox(r, canvasBox, yr, yt)
		yr = bc.setRangeDomains(canvasBox, yr)
	}

	bc.drawBars(r, canvasBox, yr)
	bc.drawXAxis(r, canvasBox)
	bc.drawYAxis(r, canvasBox, yr, yt)

	bc.drawTitle(r)
	for _, a := range bc.Elements {
		a(r, canvasBox, bc.styleDefaultsElements())
	}

	return r.Save(w)
}

func (bc BarChart) getRanges() Range {
	var yrange Range
	if bc.YAxis.Range != nil && !bc.YAxis.Range.IsZero() {
		yrange = bc.YAxis.Range
	} else {
		yrange = &ContinuousRange{}
	}

	if !yrange.IsZero() {
		return yrange
	}

	if len(bc.YAxis.Ticks) > 0 {
		tickMin, tickMax := math.MaxFloat64, -math.MaxFloat64
		for _, t := range bc.YAxis.Ticks {
			tickMin = math.Min(tickMin, t.Value)
			tickMax = math.Max(tickMax, t.Value)
		}
		yrange.SetMin(tickMin)
		yrange.SetMax(tickMax)
		return yrange
	}

	min, max := math.MaxFloat64, -math.MaxFloat64
	for _, b := range bc.Bars {
		min = math.Min(b.Value, min)
		max = math.Max(b.Value, max)
	}

	yrange.SetMin(min)
	yrange.SetMax(max)

	return yrange
}

func (bc BarChart) drawBackground(r Renderer) {
	Draw.Box(r, Box{
		Right:  bc.GetWidth(),
		Bottom: bc.GetHeight(),
	}, bc.getBackgroundStyle())
}

func (bc BarChart) drawBars(r Renderer, canvasBox Box, yr Range) {
	xoffset := canvasBox.Left

	width, spacing, _ := bc.calculateScaledTotalWidth(canvasBox)
	bs2 := spacing >> 1

	var barBox Box
	var bxl, bxr, by int
	for index, bar := range bc.Bars {
		bxl = xoffset + bs2
		bxr = bxl + width

		by = canvasBox.Bottom - yr.Translate(bar.Value)

		barBox = Box{
			Top:    by,
			Left:   bxl,
			Right:  bxr,
			Bottom: canvasBox.Bottom,
		}

		Draw.Box(r, barBox, bar.Style.InheritFrom(bc.styleDefaultsBar(index)))

		xoffset += width + spacing
	}
}

func (bc BarChart) drawXAxis(r Renderer, canvasBox Box) {
	if bc.XAxis.Show {
		axisStyle := bc.XAxis.InheritFrom(bc.styleDefaultsAxes())
		axisStyle.WriteToRenderer(r)

		width, spacing, _ := bc.calculateScaledTotalWidth(canvasBox)

		r.MoveTo(canvasBox.Left, canvasBox.Bottom)
		r.LineTo(canvasBox.Right, canvasBox.Bottom)
		r.Stroke()

		r.MoveTo(canvasBox.Left, canvasBox.Bottom)
		r.LineTo(canvasBox.Left, canvasBox.Bottom+DefaultVerticalTickHeight)
		r.Stroke()

		cursor := canvasBox.Left
		for index, bar := range bc.Bars {
			barLabelBox := Box{
				Top:    canvasBox.Bottom + DefaultXAxisMargin,
				Left:   cursor,
				Right:  cursor + width + spacing,
				Bottom: bc.GetHeight(),
			}

			if len(bar.Label) > 0 {
				Draw.TextWithin(r, bar.Label, barLabelBox, axisStyle)
			}

			axisStyle.WriteToRenderer(r)
			if index < len(bc.Bars)-1 {
				r.MoveTo(barLabelBox.Right, canvasBox.Bottom)
				r.LineTo(barLabelBox.Right, canvasBox.Bottom+DefaultVerticalTickHeight)
				r.Stroke()
			}
			cursor += width + spacing
		}
	}
}

func (bc BarChart) drawYAxis(r Renderer, canvasBox Box, yr Range, ticks []Tick) {
	if bc.YAxis.Style.Show {
		axisStyle := bc.YAxis.Style.InheritFrom(bc.styleDefaultsAxes())
		axisStyle.WriteToRenderer(r)

		r.MoveTo(canvasBox.Right, canvasBox.Top)
		r.LineTo(canvasBox.Right, canvasBox.Bottom)
		r.Stroke()

		r.MoveTo(canvasBox.Right, canvasBox.Bottom)
		r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
		r.Stroke()

		var ty int
		var tb Box
		for _, t := range ticks {
			ty = canvasBox.Bottom - yr.Translate(t.Value)

			axisStyle.GetStrokeOptions().WriteToRenderer(r)
			r.MoveTo(canvasBox.Right, ty)
			r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, ty)
			r.Stroke()

			axisStyle.GetTextOptions().WriteToRenderer(r)
			tb = r.MeasureText(t.Label)
			Draw.Text(r, t.Label, canvasBox.Right+DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle)
		}

	}
}

func (bc BarChart) drawTitle(r Renderer) {
	if len(bc.Title) > 0 && bc.TitleStyle.Show {
		Draw.TextWithin(r, bc.Title, bc.box(), bc.styleDefaultsTitle())
	}
}

func (bc BarChart) hasAxes() bool {
	return bc.YAxis.Style.Show
}

func (bc BarChart) setRangeDomains(canvasBox Box, yr Range) Range {
	yr.SetDomain(canvasBox.Height())
	return yr
}

func (bc BarChart) getDefaultCanvasBox() Box {
	return bc.box()
}

func (bc BarChart) getValueFormatters() ValueFormatter {
	if bc.YAxis.ValueFormatter != nil {
		return bc.YAxis.ValueFormatter
	}
	return FloatValueFormatter
}

func (bc BarChart) getAxesTicks(r Renderer, yr Range, yf ValueFormatter) (yticks []Tick) {
	if bc.YAxis.Style.Show {
		yticks = bc.YAxis.GetTicks(r, yr, bc.styleDefaultsAxes(), yf)
	}
	return
}

func (bc BarChart) calculateEffectiveBarSpacing(canvasBox Box) int {
	totalWithBaseSpacing := bc.calculateTotalBarWidth(bc.GetBarWidth(), bc.GetBarSpacing())
	if totalWithBaseSpacing > canvasBox.Width() {
		lessBarWidths := canvasBox.Width() - (len(bc.Bars) * bc.GetBarWidth())
		if lessBarWidths > 0 {
			return int(math.Ceil(float64(lessBarWidths) / float64(len(bc.Bars))))
		}
		return 0
	}
	return bc.GetBarSpacing()
}

func (bc BarChart) calculateEffectiveBarWidth(canvasBox Box, spacing int) int {
	totalWithBaseWidth := bc.calculateTotalBarWidth(bc.GetBarWidth(), spacing)
	if totalWithBaseWidth > canvasBox.Width() {
		totalLessBarSpacings := canvasBox.Width() - (len(bc.Bars) * spacing)
		if totalLessBarSpacings > 0 {
			return int(math.Ceil(float64(totalLessBarSpacings) / float64(len(bc.Bars))))
		}
		return 0
	}
	return bc.GetBarWidth()
}

func (bc BarChart) calculateTotalBarWidth(barWidth, spacing int) int {
	return len(bc.Bars) * (barWidth + spacing)
}

func (bc BarChart) calculateScaledTotalWidth(canvasBox Box) (width, spacing, total int) {
	spacing = bc.calculateEffectiveBarSpacing(canvasBox)
	width = bc.calculateEffectiveBarWidth(canvasBox, spacing)
	total = bc.calculateTotalBarWidth(width, spacing)
	return
}

func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range, yticks []Tick) Box {
	axesOuterBox := canvasBox.Clone()

	_, _, totalWidth := bc.calculateScaledTotalWidth(canvasBox)

	if bc.XAxis.Show {
		xaxisHeight := DefaultVerticalTickHeight

		axisStyle := bc.XAxis.InheritFrom(bc.styleDefaultsAxes())
		axisStyle.WriteToRenderer(r)

		cursor := canvasBox.Left
		for _, bar := range bc.Bars {
			if len(bar.Label) > 0 {
				barLabelBox := Box{
					Top:    canvasBox.Bottom + DefaultXAxisMargin,
					Left:   cursor,
					Right:  cursor + bc.GetBarWidth() + bc.GetBarSpacing(),
					Bottom: bc.GetHeight(),
				}
				lines := Text.WrapFit(r, bar.Label, barLabelBox.Width(), axisStyle)
				linesBox := Text.MeasureLines(r, lines, axisStyle)

				xaxisHeight = Math.MaxInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
			}
		}

		xbox := Box{
			Top:    canvasBox.Top,
			Left:   canvasBox.Left,
			Right:  canvasBox.Left + totalWidth,
			Bottom: bc.GetHeight() - xaxisHeight,
		}

		axesOuterBox = axesOuterBox.Grow(xbox)
	}

	if bc.YAxis.Style.Show {
		axesBounds := bc.YAxis.Measure(r, canvasBox, yrange, bc.styleDefaultsAxes(), yticks)
		axesOuterBox = axesOuterBox.Grow(axesBounds)
	}

	return canvasBox.OuterConstrain(bc.box(), axesOuterBox)
}

// box returns the chart bounds as a box.
func (bc BarChart) box() Box {
	dpr := bc.Background.Padding.GetRight(10)
	dpb := bc.Background.Padding.GetBottom(50)

	return Box{
		Top:    20,
		Left:   20,
		Right:  bc.GetWidth() - dpr,
		Bottom: bc.GetHeight() - dpb,
	}
}

func (bc BarChart) getBackgroundStyle() Style {
	return bc.Background.InheritFrom(bc.styleDefaultsBackground())
}

func (bc BarChart) styleDefaultsBackground() Style {
	return Style{
		FillColor:   DefaultBackgroundColor,
		StrokeColor: DefaultBackgroundStrokeColor,
		StrokeWidth: DefaultStrokeWidth,
	}
}

func (bc BarChart) styleDefaultsBar(index int) Style {
	return Style{
		StrokeColor: GetAlternateColor(index),
		StrokeWidth: 3.0,
		FillColor:   GetAlternateColor(index),
	}
}

func (bc BarChart) styleDefaultsTitle() Style {
	return bc.TitleStyle.InheritFrom(Style{
		FontColor:           DefaultTextColor,
		Font:                bc.GetFont(),
		FontSize:            bc.getTitleFontSize(),
		TextHorizontalAlign: TextHorizontalAlignCenter,
		TextVerticalAlign:   TextVerticalAlignTop,
		TextWrap:            TextWrapWord,
	})
}

func (bc BarChart) getTitleFontSize() float64 {
	effectiveDimension := Math.MinInt(bc.GetWidth(), bc.GetHeight())
	if effectiveDimension >= 2048 {
		return 48
	} else if effectiveDimension >= 1024 {
		return 24
	} else if effectiveDimension >= 512 {
		return 18
	} else if effectiveDimension >= 256 {
		return 12
	}
	return 10
}

func (bc BarChart) styleDefaultsAxes() Style {
	return Style{
		StrokeColor:         DefaultAxisColor,
		Font:                bc.GetFont(),
		FontSize:            DefaultAxisFontSize,
		FontColor:           DefaultAxisColor,
		TextHorizontalAlign: TextHorizontalAlignCenter,
		TextVerticalAlign:   TextVerticalAlignTop,
		TextWrap:            TextWrapWord,
	}
}

func (bc BarChart) styleDefaultsElements() Style {
	return Style{
		Font: bc.GetFont(),
	}
}
<commit_msg>min instead of max height for bar xaxis height<commit_after>package chart

import (
	"errors"
	"io"
	"math"

	"github.com/golang/freetype/truetype"
)

// BarChart is a chart that draws bars on a range.
type BarChart struct {
	Title      string
	TitleStyle Style

	Width  int
	Height int
	DPI    float64

	BarWidth int

	Background Style
	Canvas     Style

	XAxis Style
	YAxis YAxis

	BarSpacing int

	Font        *truetype.Font
	defaultFont *truetype.Font

	Bars     []Value
	Elements []Renderable
}

// GetDPI returns the dpi for the chart.
func (bc BarChart) GetDPI() float64 {
	if bc.DPI == 0 {
		return DefaultDPI
	}
	return bc.DPI
}

// GetFont returns the text font.
func (bc BarChart) GetFont() *truetype.Font {
	if bc.Font == nil {
		return bc.defaultFont
	}
	return bc.Font
}

// GetWidth returns the chart width or the default value.
func (bc BarChart) GetWidth() int {
	if bc.Width == 0 {
		return DefaultChartWidth
	}
	return bc.Width
}

// GetHeight returns the chart height or the default value.
func (bc BarChart) GetHeight() int {
	if bc.Height == 0 {
		return DefaultChartHeight
	}
	return bc.Height
}

// GetBarSpacing returns the spacing between bars.
func (bc BarChart) GetBarSpacing() int {
	if bc.BarSpacing == 0 {
		return DefaultBarSpacing
	}
	return bc.BarSpacing
}

// GetBarWidth returns the default bar width.
func (bc BarChart) GetBarWidth() int {
	if bc.BarWidth == 0 {
		return DefaultBarWidth
	}
	return bc.BarWidth
}

// Render renders the chart with the given renderer to the given io.Writer.
func (bc BarChart) Render(rp RendererProvider, w io.Writer) error {
	if len(bc.Bars) == 0 {
		return errors.New("Please provide at least one bar.")
	}

	r, err := rp(bc.GetWidth(), bc.GetHeight())
	if err != nil {
		return err
	}

	if bc.Font == nil {
		defaultFont, err := GetDefaultFont()
		if err != nil {
			return err
		}
		bc.defaultFont = defaultFont
	}
	r.SetDPI(bc.GetDPI())

	bc.drawBackground(r)

	var canvasBox Box
	var yt []Tick
	var yr Range
	var yf ValueFormatter

	canvasBox = bc.getDefaultCanvasBox()
	yr = bc.getRanges()
	yr = bc.setRangeDomains(canvasBox, yr)
	yf = bc.getValueFormatters()

	if bc.hasAxes() {
		yt = bc.getAxesTicks(r, yr, yf)
		canvasBox = bc.getAdjustedCanvasBox(r, canvasBox, yr, yt)
		yr = bc.setRangeDomains(canvasBox, yr)
	}

	bc.drawBars(r, canvasBox, yr)
	bc.drawXAxis(r, canvasBox)
	bc.drawYAxis(r, canvasBox, yr, yt)

	bc.drawTitle(r)
	for _, a := range bc.Elements {
		a(r, canvasBox, bc.styleDefaultsElements())
	}

	return r.Save(w)
}

func (bc BarChart) getRanges() Range {
	var yrange Range
	if bc.YAxis.Range != nil && !bc.YAxis.Range.IsZero() {
		yrange = bc.YAxis.Range
	} else {
		yrange = &ContinuousRange{}
	}

	if !yrange.IsZero() {
		return yrange
	}

	if len(bc.YAxis.Ticks) > 0 {
		tickMin, tickMax := math.MaxFloat64, -math.MaxFloat64
		for _, t := range bc.YAxis.Ticks {
			tickMin = math.Min(tickMin, t.Value)
			tickMax = math.Max(tickMax, t.Value)
		}
		yrange.SetMin(tickMin)
		yrange.SetMax(tickMax)
		return yrange
	}

	min, max := math.MaxFloat64, -math.MaxFloat64
	for _, b := range bc.Bars {
		min = math.Min(b.Value, min)
		max = math.Max(b.Value, max)
	}

	yrange.SetMin(min)
	yrange.SetMax(max)

	return yrange
}

func (bc BarChart) drawBackground(r Renderer) {
	Draw.Box(r, Box{
		Right:  bc.GetWidth(),
		Bottom: bc.GetHeight(),
	}, bc.getBackgroundStyle())
}

func (bc BarChart) drawBars(r Renderer, canvasBox Box, yr Range) {
	xoffset := canvasBox.Left

	width, spacing, _ := bc.calculateScaledTotalWidth(canvasBox)
	bs2 := spacing >> 1

	var barBox Box
	var bxl, bxr, by int
	for index, bar := range bc.Bars {
		bxl = xoffset + bs2
		bxr = bxl + width

		by = canvasBox.Bottom - yr.Translate(bar.Value)

		barBox = Box{
			Top:    by,
			Left:   bxl,
			Right:  bxr,
			Bottom: canvasBox.Bottom,
		}

		Draw.Box(r, barBox, bar.Style.InheritFrom(bc.styleDefaultsBar(index)))

		xoffset += width + spacing
	}
}

func (bc BarChart) drawXAxis(r Renderer, canvasBox Box) {
	if bc.XAxis.Show {
		axisStyle := bc.XAxis.InheritFrom(bc.styleDefaultsAxes())
		axisStyle.WriteToRenderer(r)

		width, spacing, _ := bc.calculateScaledTotalWidth(canvasBox)

		r.MoveTo(canvasBox.Left, canvasBox.Bottom)
		r.LineTo(canvasBox.Right, canvasBox.Bottom)
		r.Stroke()

		r.MoveTo(canvasBox.Left, canvasBox.Bottom)
		r.LineTo(canvasBox.Left, canvasBox.Bottom+DefaultVerticalTickHeight)
		r.Stroke()

		cursor := canvasBox.Left
		for index, bar := range bc.Bars {
			barLabelBox := Box{
				Top:    canvasBox.Bottom + DefaultXAxisMargin,
				Left:   cursor,
				Right:  cursor + width + spacing,
				Bottom: bc.GetHeight(),
			}

			if len(bar.Label) > 0 {
				Draw.TextWithin(r, bar.Label, barLabelBox, axisStyle)
			}

			axisStyle.WriteToRenderer(r)
			if index < len(bc.Bars)-1 {
				r.MoveTo(barLabelBox.Right, canvasBox.Bottom)
				r.LineTo(barLabelBox.Right, canvasBox.Bottom+DefaultVerticalTickHeight)
				r.Stroke()
			}
			cursor += width + spacing
		}
	}
}

func (bc BarChart) drawYAxis(r Renderer, canvasBox Box, yr Range, ticks []Tick) {
	if bc.YAxis.Style.Show {
		axisStyle := bc.YAxis.Style.InheritFrom(bc.styleDefaultsAxes())
		axisStyle.WriteToRenderer(r)

		r.MoveTo(canvasBox.Right, canvasBox.Top)
		r.LineTo(canvasBox.Right, canvasBox.Bottom)
		r.Stroke()

		r.MoveTo(canvasBox.Right, canvasBox.Bottom)
		r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, canvasBox.Bottom)
		r.Stroke()

		var ty int
		var tb Box
		for _, t := range ticks {
			ty = canvasBox.Bottom - yr.Translate(t.Value)

			axisStyle.GetStrokeOptions().WriteToRenderer(r)
			r.MoveTo(canvasBox.Right, ty)
			r.LineTo(canvasBox.Right+DefaultHorizontalTickWidth, ty)
			r.Stroke()

			axisStyle.GetTextOptions().WriteToRenderer(r)
			tb = r.MeasureText(t.Label)
			Draw.Text(r, t.Label, canvasBox.Right+DefaultYAxisMargin+5, ty+(tb.Height()>>1), axisStyle)
		}

	}
}

func (bc BarChart) drawTitle(r Renderer) {
	if len(bc.Title) > 0 && bc.TitleStyle.Show {
		Draw.TextWithin(r, bc.Title, bc.box(), bc.styleDefaultsTitle())
	}
}

func (bc BarChart) hasAxes() bool {
	return bc.YAxis.Style.Show
}

func (bc BarChart) setRangeDomains(canvasBox Box, yr Range) Range {
	yr.SetDomain(canvasBox.Height())
	return yr
}

func (bc BarChart) getDefaultCanvasBox() Box {
	return bc.box()
}

func (bc BarChart) getValueFormatters() ValueFormatter {
	if bc.YAxis.ValueFormatter != nil {
		return bc.YAxis.ValueFormatter
	}
	return FloatValueFormatter
}

func (bc BarChart) getAxesTicks(r Renderer, yr Range, yf ValueFormatter) (yticks []Tick) {
	if bc.YAxis.Style.Show {
		yticks = bc.YAxis.GetTicks(r, yr, bc.styleDefaultsAxes(), yf)
	}
	return
}

func (bc BarChart) calculateEffectiveBarSpacing(canvasBox Box) int {
	totalWithBaseSpacing := bc.calculateTotalBarWidth(bc.GetBarWidth(), bc.GetBarSpacing())
	if totalWithBaseSpacing > canvasBox.Width() {
		lessBarWidths := canvasBox.Width() - (len(bc.Bars) * bc.GetBarWidth())
		if lessBarWidths > 0 {
			return int(math.Ceil(float64(lessBarWidths) / float64(len(bc.Bars))))
		}
		return 0
	}
	return bc.GetBarSpacing()
}

func (bc BarChart) calculateEffectiveBarWidth(canvasBox Box, spacing int) int {
	totalWithBaseWidth := bc.calculateTotalBarWidth(bc.GetBarWidth(), spacing)
	if totalWithBaseWidth > canvasBox.Width() {
		totalLessBarSpacings := canvasBox.Width() - (len(bc.Bars) * spacing)
		if totalLessBarSpacings > 0 {
			return int(math.Ceil(float64(totalLessBarSpacings) / float64(len(bc.Bars))))
		}
		return 0
	}
	return bc.GetBarWidth()
}

func (bc BarChart) calculateTotalBarWidth(barWidth, spacing int) int {
	return len(bc.Bars) * (barWidth + spacing)
}

func (bc BarChart) calculateScaledTotalWidth(canvasBox Box) (width, spacing, total int) {
	spacing = bc.calculateEffectiveBarSpacing(canvasBox)
	width = bc.calculateEffectiveBarWidth(canvasBox, spacing)
	total = bc.calculateTotalBarWidth(width, spacing)
	return
}

func (bc BarChart) getAdjustedCanvasBox(r Renderer, canvasBox Box, yrange Range, yticks []Tick) Box {
	axesOuterBox := canvasBox.Clone()

	_, _, totalWidth := bc.calculateScaledTotalWidth(canvasBox)

	if bc.XAxis.Show {
		xaxisHeight := DefaultVerticalTickHeight

		axisStyle := bc.XAxis.InheritFrom(bc.styleDefaultsAxes())
		axisStyle.WriteToRenderer(r)

		cursor := canvasBox.Left
		for _, bar := range bc.Bars {
			if len(bar.Label) > 0 {
				barLabelBox := Box{
					Top:    canvasBox.Bottom + DefaultXAxisMargin,
					Left:   cursor,
					Right:  cursor + bc.GetBarWidth() + bc.GetBarSpacing(),
					Bottom: bc.GetHeight(),
				}
				lines := Text.WrapFit(r, bar.Label, barLabelBox.Width(), axisStyle)
				linesBox := Text.MeasureLines(r, lines, axisStyle)

				xaxisHeight = Math.MinInt(linesBox.Height()+(2*DefaultXAxisMargin), xaxisHeight)
			}
		}

		xbox := Box{
			Top:    canvasBox.Top,
			Left:   canvasBox.Left,
			Right:  canvasBox.Left + totalWidth,
			Bottom: bc.GetHeight() - xaxisHeight,
		}

		axesOuterBox = axesOuterBox.Grow(xbox)
	}

	if bc.YAxis.Style.Show {
		axesBounds := bc.YAxis.Measure(r, canvasBox, yrange, bc.styleDefaultsAxes(), yticks)
		axesOuterBox = axesOuterBox.Grow(axesBounds)
	}

	return canvasBox.OuterConstrain(bc.box(), axesOuterBox)
}

// box returns the chart bounds as a box.
func (bc BarChart) box() Box {
	dpr := bc.Background.Padding.GetRight(10)
	dpb := bc.Background.Padding.GetBottom(50)

	return Box{
		Top:    20,
		Left:   20,
		Right:  bc.GetWidth() - dpr,
		Bottom: bc.GetHeight() - dpb,
	}
}

func (bc BarChart) getBackgroundStyle() Style {
	return bc.Background.InheritFrom(bc.styleDefaultsBackground())
}

func (bc BarChart) styleDefaultsBackground() Style {
	return Style{
		FillColor:   DefaultBackgroundColor,
		StrokeColor: DefaultBackgroundStrokeColor,
		StrokeWidth: DefaultStrokeWidth,
	}
}

func (bc BarChart) styleDefaultsBar(index int) Style {
	return Style{
		StrokeColor: GetAlternateColor(index),
		StrokeWidth: 3.0,
		FillColor:   GetAlternateColor(index),
	}
}

func (bc BarChart) styleDefaultsTitle() Style {
	return bc.TitleStyle.InheritFrom(Style{
		FontColor:           DefaultTextColor,
		Font:                bc.GetFont(),
		FontSize:            bc.getTitleFontSize(),
		TextHorizontalAlign: TextHorizontalAlignCenter,
		TextVerticalAlign:   TextVerticalAlignTop,
		TextWrap:            TextWrapWord,
	})
}

func (bc BarChart) getTitleFontSize() float64 {
	effectiveDimension := Math.MinInt(bc.GetWidth(), bc.GetHeight())
	if effectiveDimension >= 2048 {
		return 48
	} else if effectiveDimension >= 1024 {
		return 24
	} else if effectiveDimension >= 512 {
		return 18
	} else if effectiveDimension >= 256 {
		return 12
	}
	return 10
}

func (bc BarChart) styleDefaultsAxes() Style {
	return Style{
		StrokeColor:         DefaultAxisColor,
		Font:                bc.GetFont(),
		FontSize:            DefaultAxisFontSize,
		FontColor:           DefaultAxisColor,
		TextHorizontalAlign: TextHorizontalAlignCenter,
		TextVerticalAlign:   TextVerticalAlignTop,
		TextWrap:            TextWrapWord,
	}
}

func (bc BarChart) styleDefaultsElements() Style {
	return Style{
		Font: bc.GetFont(),
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package config

import (
	"testing"
)

func TestValidateFlags(t *testing.T) {
	cfg := NewConfig()
	cfg.LogFormat = "test"
	if err := cfg.Validate(); err == nil {
		t.Errorf("unsupported log format should fail: %s", cfg.LogFormat)
	}
	cfg.LogFormat = ""
	if err := cfg.Validate(); err == nil {
		t.Errorf("unsupported log format should fail: %s", cfg.LogFormat)
	}
	for _, format := range []string{"text", "json"} {
		cfg.LogFormat = format
		if err := cfg.Validate(); err != nil {
			t.Errorf("supported log format: %s should not fail", format)
		}
	}
}
<commit_msg>fix boilerplate for config_test<commit_after>/*
Copyright 2017 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

import (
	"testing"
)

func TestValidateFlags(t *testing.T) {
	cfg := NewConfig()
	cfg.LogFormat = "test"
	if err := cfg.Validate(); err == nil {
		t.Errorf("unsupported log format should fail: %s", cfg.LogFormat)
	}
	cfg.LogFormat = ""
	if err := cfg.Validate(); err == nil {
		t.Errorf("unsupported log format should fail: %s", cfg.LogFormat)
	}
	for _, format := range []string{"text", "json"} {
		cfg.LogFormat = format
		if err := cfg.Validate(); err != nil {
			t.Errorf("supported log format: %s should not fail", format)
		}
	}
}
<|endoftext|>


<commit_before>package config

import (
	"testing"
)

func TestLoadConfig(t *testing.T) {
	c, err := New("../tests/awsnycast.yaml")
	if err != nil {
		t.Log(err)
		t.Fail()
	}
	if c == nil {
		t.Fail()
	}
}

func TestLoadConfigFails(t *testing.T) {
	_, err := New("../tests/doesnotexist.yaml")
	if err == nil {
		t.Fail()
	}
}

func TestLoadConfigHealthchecks(t *testing.T) {
	c, _ := New("../tests/awsnycast.yaml")
	if c.Healthchecks == nil {
		t.Fail()
	}
}
<commit_msg>Test YAML is read in as expected<commit_after>package config

import (
	"testing"
)

func TestLoadConfig(t *testing.T) {
	c, err := New("../tests/awsnycast.yaml")
	if err != nil {
		t.Log(err)
		t.Fail()
	}
	if c == nil {
		t.Fail()
	}
}

func TestLoadConfigFails(t *testing.T) {
	_, err := New("../tests/doesnotexist.yaml")
	if err == nil {
		t.Fail()
	}
}

func TestLoadConfigHealthchecks(t *testing.T) {
	c, _ := New("../tests/awsnycast.yaml")
	if c.Healthchecks == nil {
		t.Fail()
	}
	h, ok := c.Healthchecks["public"]
	if !ok {
		t.Fail()
	}
	if h.Type != "ping" {
		t.Fail()
	}
	if h.Destination != "8.8.8.8" {
		t.Fail()
	}
	if h.Rise != 2 {
		t.Fail()
	}
	if h.Fall != 10 {
		t.Fail()
	}
	if h.Every != 1 {
		t.Fail()
	}
	a, ok := c.RouteTables["a"]
	if !ok {
		t.Log("RouteTables a not ok")
		t.Fail()
	}
	if a.Find.Type != "by_tag" {
		t.Fail()
	}
	if v, ok := a.Find.Config["key"]; ok {
		if v != "Name" {
			t.Fail()
		}
	} else {
		t.Fail()
	}
	if v, ok := a.Find.Config["value"]; ok {
		if v != "private a" {
			t.Fail()
		}
	} else {
		t.Fail()
	}
	routes := a.UpsertRoutes
	if len(routes) != 2 {
		t.Fail()
	}
	for _, route := range routes {
		if route.Cidr == "0.0.0.0/0" || route.Cidr == "192.168.1.1/32" {
			if route.Instance != "SELF" {
				t.Fail()
			}
			if route.Cidr == "0.0.0.0/0" {
				if route.Healthcheck != "public" {
					t.Fail()
				}
			} else {
				if route.Healthcheck != "localservice" {
					t.Fail()
				}
			}
		} else {
			t.Fail()
		}
	}
	b, ok := c.RouteTables["b"]
	if !ok {
		t.Fail()
	}
	if b.Find.Type != "by_tag" {
		t.Fail()
	}
}
<|endoftext|>


<commit_before>/*
 Copyright (c) 2015 Magnus Skjegstad <magnus@skjegstad.com>

 Permission to use, copy, modify, and distribute this software for any
 purpose with or without fee is hereby granted, provided that the above
 copyright notice and this permission notice appear in all copies.

 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

package main

import (
	"encoding/hex"
	"fmt"
	"net/url"

	"../irmin"
)

// irmin init -d -v --root /tmp/irmin/test -a http://:8080

func main() {
	uri, err := url.Parse("http://127.0.0.1:8080")
	if err != nil {
		panic(err)
	}

	r := irmin.Create(uri, "api-tester")
	{ // get version
		v, err := r.Version()
		if err != nil {
			panic(err)
		}
		fmt.Printf("version: %s\n", v)
	}
	{ // get head
		v, err := r.Head()
		if err != nil {
			panic(err)
		}
		fmt.Printf("head: %s\n", hex.EncodeToString(v))
	}
	{ // list commands
		fmt.Printf("supported commands:\n")
		s, err := r.AvailableCommands()
		if err != nil {
			panic(err)
		}
		for i, v := range s {
			fmt.Printf("%d: %s\n", i, v)
		}
	}
	{ // list
		paths, err := r.List(irmin.ParsePath("/a"))
		if err != nil {
			panic(err)
		}
		fmt.Printf("list /\n")
		for i, v := range paths {
			fmt.Printf("%d: %s\n", i, v.String())
		}
	}
	{ // iter
		var ch <-chan *irmin.Path
		if ch, err = r.Iter(); err != nil {
			panic(err)
		}

		for p := range ch {
			fmt.Printf("iter: %s\n", (*p).String())
		}
	}
	{ // iter on head
		head, err := r.Head()
		if err != nil {
			panic(err)
		}
		t := r.FromTree(hex.EncodeToString(head))
		var ch <-chan *irmin.Path
		if ch, err = t.Iter(); err != nil {
			panic(err)
		}

		for p := range ch {
			fmt.Printf("iter from HEAD: %s\n", (*p).String())
		}
	}
	{ // iter + read
		var ch <-chan *irmin.Path
		if ch, err = r.Iter(); err != nil {
			panic(err)
		}

		for p := range ch {
			d, err := r.ReadString(*p)
			if err != nil {
				panic(err)
			}
			fmt.Printf("%s=%s\n", (*p).String(), d)
		}
	}
	{ // update + read
		key := "g"
		fmt.Printf("update %s=hello world\n", key)
		data := []byte("Hello \"world")
		hash, err := r.Update(r.NewTask("update key"), irmin.ParsePath(key), data)
		if err != nil {
			panic(err)
		}
		fmt.Printf("update hash: %s\n", hash)
		fmt.Printf("read %s\n", key)
		d, err := r.ReadString(irmin.ParsePath(key))
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s=%s\n", key, d)
	}
	/* compare-and-set is not yet implemented in irmin, see https://github.com/mirage/irmin/issues/288
	{ // compare-and-set
		key := "g"
		oldData := []byte("Hello world")
		newData := []byte("asdf")
		fmt.Printf("compare-and-set %s=%s to %s\n", key, oldData, newData)
		hash, err := r.CompareAndSet(r.NewTask("compare-and-set key"), irmin.ParsePath(key), &oldData, &newData)
		if err != nil {
			panic(err)
		}
		fmt.Printf("compare-and-set hash: %s\n", hash)
		fmt.Printf("read %s\n", key)
		d, err := r.ReadString(irmin.ParsePath(key))
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s=%s\n", key, d)
	}
	*/
}
<commit_msg>Move head test after update to avoid error if db is empty<commit_after>/*
 Copyright (c) 2015 Magnus Skjegstad <magnus@skjegstad.com>

 Permission to use, copy, modify, and distribute this software for any
 purpose with or without fee is hereby granted, provided that the above
 copyright notice and this permission notice appear in all copies.

 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

package main

import (
	"encoding/hex"
	"fmt"
	"net/url"

	"../irmin"
)

// irmin init -d -v --root /tmp/irmin/test -a http://:8080

func main() {
	uri, err := url.Parse("http://127.0.0.1:8080")
	if err != nil {
		panic(err)
	}

	r := irmin.Create(uri, "api-tester")
	{ // get version
		v, err := r.Version()
		if err != nil {
			panic(err)
		}
		fmt.Printf("version: %s\n", v)
	}
	{ // list commands
		fmt.Printf("supported commands:\n")
		s, err := r.AvailableCommands()
		if err != nil {
			panic(err)
		}
		for i, v := range s {
			fmt.Printf("%d: %s\n", i, v)
		}
	}
	{ // list
		paths, err := r.List(irmin.ParsePath("/a"))
		if err != nil {
			panic(err)
		}
		fmt.Printf("list /\n")
		for i, v := range paths {
			fmt.Printf("%d: %s\n", i, v.String())
		}
	}
	{ // iter
		var ch <-chan *irmin.Path
		if ch, err = r.Iter(); err != nil {
			panic(err)
		}

		for p := range ch {
			fmt.Printf("iter: %s\n", (*p).String())
		}
	}
	{ // iter on head
		head, err := r.Head()
		if err != nil {
			panic(err)
		}
		t := r.FromTree(hex.EncodeToString(head))
		var ch <-chan *irmin.Path
		if ch, err = t.Iter(); err != nil {
			panic(err)
		}

		for p := range ch {
			fmt.Printf("iter from HEAD: %s\n", (*p).String())
		}
	}
	{ // iter + read
		var ch <-chan *irmin.Path
		if ch, err = r.Iter(); err != nil {
			panic(err)
		}

		for p := range ch {
			d, err := r.ReadString(*p)
			if err != nil {
				panic(err)
			}
			fmt.Printf("%s=%s\n", (*p).String(), d)
		}
	}
	{ // update + read
		key := "g"
		fmt.Printf("update %s=hello world\n", key)
		data := []byte("Hello \"world")
		hash, err := r.Update(r.NewTask("update key"), irmin.ParsePath(key), data)
		if err != nil {
			panic(err)
		}
		fmt.Printf("update hash: %s\n", hash)
		fmt.Printf("read %s\n", key)
		d, err := r.ReadString(irmin.ParsePath(key))
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s=%s\n", key, d)
	}
	{ // get head
		v, err := r.Head()
		if err != nil {
			panic(err)
		}
		fmt.Printf("head: %s\n", hex.EncodeToString(v))
	}
	/* compare-and-set is not yet implemented in irmin, see https://github.com/mirage/irmin/issues/288
	{ // compare-and-set
		key := "g"
		oldData := []byte("Hello world")
		newData := []byte("asdf")
		fmt.Printf("compare-and-set %s=%s to %s\n", key, oldData, newData)
		hash, err := r.CompareAndSet(r.NewTask("compare-and-set key"), irmin.ParsePath(key), &oldData, &newData)
		if err != nil {
			panic(err)
		}
		fmt.Printf("compare-and-set hash: %s\n", hash)
		fmt.Printf("read %s\n", key)
		d, err := r.ReadString(irmin.ParsePath(key))
		if err != nil {
			panic(err)
		}
		fmt.Printf("%s=%s\n", key, d)
	}
	*/
}
<|endoftext|>


<commit_before>package execstreamer

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
)

//ExecStreamer is the streamer interface (built by the ExecStreamerBuilder)
type ExecStreamer interface {
	ExecAndWait() error
}

type execStreamer struct {
	ExecutorName string
	Exe          string
	Args         []string
	Dir          string
	Env          []string

	StdoutWriter io.Writer
	StdoutPrefix string

	StderrWriter io.Writer
	StderrPrefix string

	AutoFlush bool
}

func (e *execStreamer) flushIfEnabled(writer io.Writer) {
	if e.AutoFlush {
		if flusher, ok := writer.(http.Flusher); ok {
			flusher.Flush()
		}
	}
}

//ExecAndWait will execute the command using the given executor and wait until completion
func (e *execStreamer) ExecAndWait() error {
	x, err := NewExecutorFromName(e.ExecutorName)
	if err != nil {
		return err
	}

	cmd := x.GetCommand(e.Exe, e.Args...)

	if e.Dir != "" {
		cmd.Dir = e.Dir
	}
	if len(e.Env) > 0 {
		cmd.Env = e.Env
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return err
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdoutScanner := bufio.NewScanner(stdout)
	go func() {
		for stdoutScanner.Scan() {
			fmt.Fprintf(e.StdoutWriter, "%s%s\n", e.StdoutPrefix, stdoutScanner.Text())
			e.flushIfEnabled(e.StdoutWriter)
		}
	}()

	stderrScanner := bufio.NewScanner(stderr)
	go func() {
		for stderrScanner.Scan() {
			fmt.Fprintf(e.StderrWriter, "%s%s\n", e.StderrPrefix, stderrScanner.Text())
			e.flushIfEnabled(e.StderrWriter)
		}
	}()

	err = cmd.Start()
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Add `StartExec() (*exec.Cmd, error)` method<commit_after>package execstreamer

import (
	"bufio"
	"fmt"
	"io"
	"net/http"
	"os/exec"
)

//ExecStreamer is the streamer interface (built by the ExecStreamerBuilder)
type ExecStreamer interface {
	StartExec() (*exec.Cmd, error)
	ExecAndWait() error
}

type execStreamer struct {
	ExecutorName string
	Exe          string
	Args         []string
	Dir          string
	Env          []string

	StdoutWriter io.Writer
	StdoutPrefix string

	StderrWriter io.Writer
	StderrPrefix string

	AutoFlush bool
}

func (e *execStreamer) flushIfEnabled(writer io.Writer) {
	if e.AutoFlush {
		if flusher, ok := writer.(http.Flusher); ok {
			flusher.Flush()
		}
	}
}

//StartExec will execute the command using the given executor and return (without waiting for completion) with the exec.Cmd
func (e *execStreamer) StartExec() (*exec.Cmd, error) {
	x, err := NewExecutorFromName(e.ExecutorName)
	if err != nil {
		return nil, err
	}

	cmd := x.GetCommand(e.Exe, e.Args...)

	if e.Dir != "" {
		cmd.Dir = e.Dir
	}
	if len(e.Env) > 0 {
		cmd.Env = e.Env
	}

	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	stderr, err := cmd.StderrPipe()
	if err != nil {
		return nil, err
	}

	stdoutScanner := bufio.NewScanner(stdout)
	go func() {
		for stdoutScanner.Scan() {
			fmt.Fprintf(e.StdoutWriter, "%s%s\n", e.StdoutPrefix, stdoutScanner.Text())
			e.flushIfEnabled(e.StdoutWriter)
		}
	}()

	stderrScanner := bufio.NewScanner(stderr)
	go func() {
		for stderrScanner.Scan() {
			fmt.Fprintf(e.StderrWriter, "%s%s\n", e.StderrPrefix, stderrScanner.Text())
			e.flushIfEnabled(e.StderrWriter)
		}
	}()

	err = cmd.Start()
	if err != nil {
		return nil, err
	}

	return cmd, nil
}

//ExecAndWait will execute the command using the given executor and wait until completion
func (e *execStreamer) ExecAndWait() error {
	cmd, err := e.StartExec()
	if err != nil {
		return err
	}

	err = cmd.Wait()
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package kite

import (
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/koding/kite/config"
	"github.com/koding/kite/dnode"
	_ "github.com/koding/kite/testutil"
)

func TestMultiple(t *testing.T) {
	testDuration := time.Second * 10

	// number of available mathworker kites to be called
	kiteNumber := 3

	// number of exp kites that will call mathwork kites
	clientNumber := 10

	// ports are starting from 6000 up to 6000 + kiteNumber
	port := 6000

	fmt.Printf("Creating %d mathworker kites\n", kiteNumber)

	var transport config.Transport
	if transportName := os.Getenv("KITE_TRANSPORT"); transportName != "" {
		tr, ok := config.Transports[transportName]
		if !ok {
			t.Fatalf("transport '%s' doesn't exists", transportName)
		}

		transport = tr
	}

	for i := 0; i < kiteNumber; i++ {
		m := New("mathworker"+strconv.Itoa(i), "0.1."+strconv.Itoa(i))
		m.Config.DisableAuthentication = true
		m.Config.Transport = transport

		m.HandleFunc("square", Square)

		go http.ListenAndServe("127.0.0.1:"+strconv.Itoa(port+i), m)
	}

	// Wait until it's started
	time.Sleep(time.Second * 2)

	fmt.Printf("Creating %d exp clients\n", clientNumber)
	clients := make([]*Client, clientNumber)
	for i := 0; i < clientNumber; i++ {
		cn := New("exp"+strconv.Itoa(i), "0.0.1")
		cn.Config.Transport = transport
		c := cn.NewClient("http://127.0.0.1:" + strconv.Itoa(port+i) + "/kite")
		if err := c.Dial(); err != nil {
			t.Fatal(err)
		}

		clients[i] = c
	}

	var wg sync.WaitGroup

	fmt.Printf("Calling mathworker kites with %d conccurent clients randomly\n", clientNumber)
	timeout := time.After(testDuration)

	// every one second
	for {
		select {
		case <-time.Tick(time.Second):
			for i := 0; i < clientNumber; i++ {
				wg.Add(1)

				go func(i int, t *testing.T) {
					defer wg.Done()
					time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))

					_, err := clients[i].TellWithTimeout("square", 4*time.Second, 2)
					if err != nil {
						t.Error(err)
					}
				}(i, t)
			}
		case <-timeout:
			fmt.Println("test stopped")
			t.SkipNow()
		}

	}

	wg.Wait()
}

// Call a single method with multiple clients. This test is implemented to be
// sure the method is calling back with in the same time and not timing out.
func TestConcurrency(t *testing.T) {
	// Create a mathworker kite
	mathKite := newXhrKite("mathworker", "0.0.1")
	mathKite.Config.DisableAuthentication = true
	mathKite.HandleFunc("ping", func(r *Request) (interface{}, error) {
		time.Sleep(time.Second)
		return "pong", nil
	})
	go http.ListenAndServe("127.0.0.1:3637", mathKite)

	// Wait until it's started
	time.Sleep(time.Second)

	// number of exp kites that will call mathworker kite
	clientNumber := 10

	fmt.Printf("Creating %d exp clients\n", clientNumber)
	clients := make([]*Client, clientNumber)
	for i := 0; i < clientNumber; i++ {
		c := newXhrKite("exp", "0.0.1").NewClient("http://127.0.0.1:3637/kite")
		if err := c.Dial(); err != nil {
			t.Fatal(err)
		}

		clients[i] = c
	}

	var wg sync.WaitGroup

	for i := range clients {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			result, err := clients[i].TellWithTimeout("ping", 4*time.Second)
			if err != nil {
				t.Fatal(err)
			}

			if result.MustString() != "pong" {
				t.Errorf("Got %s want: pong", result.MustString())
			}
		}(i)
	}

	wg.Wait()
}

// Test 2 way communication between kites.
func TestKite(t *testing.T) {
	// Create a mathworker kite
	mathKite := newXhrKite("mathworker", "0.0.1")
	mathKite.Config.DisableAuthentication = true
	mathKite.HandleFunc("square", Square)
	mathKite.HandleFunc("squareCB", SquareCB)
	mathKite.HandleFunc("sleep", Sleep)
	go http.ListenAndServe("127.0.0.1:3636", mathKite)

	// Wait until it's started
	time.Sleep(time.Second)

	// Create exp2 kite
	exp2Kite := newXhrKite("exp2", "0.0.1")
	fooChan := make(chan string)
	exp2Kite.HandleFunc("foo", func(r *Request) (interface{}, error) {
		s := r.Args.One().MustString()
		t.Logf("Message received: %s\n", s)
		fooChan <- s
		return nil, nil
	})

	// exp2 connects to mathworker
	remote := exp2Kite.NewClient("http://127.0.0.1:3636/kite")
	err := remote.Dial()
	if err != nil {
		t.Fatal(err)
	}

	result, err := remote.TellWithTimeout("square", 4*time.Second, 2)
	if err != nil {
		t.Fatal(err)
	}

	number := result.MustFloat64()

	t.Logf("rpc result: %f\n", number)

	if number != 4 {
		t.Fatalf("Invalid result: %f", number)
	}

	select {
	case s := <-fooChan:
		if s != "bar" {
			t.Fatalf("Invalid message: %s", s)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("Did not get the message")
	}

	resultChan := make(chan float64, 1)
	resultCallback := func(args *dnode.Partial) {
		n := args.One().MustFloat64()
		resultChan <- n
	}

	result, err = remote.TellWithTimeout("squareCB", 4*time.Second, 3, dnode.Callback(resultCallback))
	if err != nil {
		t.Fatal(err)
	}

	select {
	case n := <-resultChan:
		if n != 9.0 {
			t.Fatalf("Unexpected result: %f", n)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("Did not get the message")
	}

	result, err = remote.TellWithTimeout("sleep", time.Second)
	if err == nil {
		t.Fatal("Did get message in 1 seconds, however the sleep method takes 2 seconds to response")
	}

	result, err = remote.Tell("sleep")
	if err != nil {
		t.Fatal(err)
	}

	if !result.MustBool() {
		t.Fatal("sleep result must be true")
	}

}

// Sleeps for 2 seconds and returns true
func Sleep(r *Request) (interface{}, error) {
	time.Sleep(time.Second * 2)
	return true, nil
}

// Returns the result. Also tests reverse call.
func Square(r *Request) (interface{}, error) {
	a := r.Args.One().MustFloat64()
	result := a * a

	r.LocalKite.Log.Info("Kite call, sending result '%f' back\n", result)

	// Reverse method call
	r.Client.Go("foo", "bar")

	return result, nil
}

// Calls the callback with the result. For testing requests with Callback.
func SquareCB(r *Request) (interface{}, error) {
	args := r.Args.MustSliceOfLength(2)
	a := args[0].MustFloat64()
	cb := args[1].MustFunction()

	result := a * a

	r.LocalKite.Log.Info("Kite call, sending result '%f' back\n", result)

	// Send the result.
	err := cb.Call(result)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func newXhrKite(name, version string) *Kite {
	k := New(name, version)
	k.Config.Transport = config.XHRPolling
	return k
}
<commit_msg>Kite: decrease kite number to 3<commit_after>package kite

import (
	"fmt"
	"math/rand"
	"net/http"
	"os"
	"strconv"
	"sync"
	"testing"
	"time"

	"github.com/koding/kite/config"
	"github.com/koding/kite/dnode"
	_ "github.com/koding/kite/testutil"
)

func TestMultiple(t *testing.T) {
	testDuration := time.Second * 10

	// number of available mathworker kites to be called
	kiteNumber := 3

	// number of exp kites that will call mathwork kites
	clientNumber := 3

	// ports are starting from 6000 up to 6000 + kiteNumber
	port := 6000

	fmt.Printf("Creating %d mathworker kites\n", kiteNumber)

	var transport config.Transport
	if transportName := os.Getenv("KITE_TRANSPORT"); transportName != "" {
		tr, ok := config.Transports[transportName]
		if !ok {
			t.Fatalf("transport '%s' doesn't exists", transportName)
		}

		transport = tr
	}

	for i := 0; i < kiteNumber; i++ {
		m := New("mathworker"+strconv.Itoa(i), "0.1."+strconv.Itoa(i))
		m.Config.DisableAuthentication = true
		m.Config.Transport = transport

		m.HandleFunc("square", Square)

		go http.ListenAndServe("127.0.0.1:"+strconv.Itoa(port+i), m)
	}

	// Wait until it's started
	time.Sleep(time.Second * 2)

	fmt.Printf("Creating %d exp clients\n", clientNumber)
	clients := make([]*Client, clientNumber)
	for i := 0; i < clientNumber; i++ {
		cn := New("exp"+strconv.Itoa(i), "0.0.1")
		cn.Config.Transport = transport
		c := cn.NewClient("http://127.0.0.1:" + strconv.Itoa(port+i) + "/kite")
		if err := c.Dial(); err != nil {
			t.Fatal(err)
		}

		clients[i] = c
	}

	var wg sync.WaitGroup

	fmt.Printf("Calling mathworker kites with %d conccurent clients randomly\n", clientNumber)
	timeout := time.After(testDuration)

	// every one second
	for {
		select {
		case <-time.Tick(time.Second):
			for i := 0; i < clientNumber; i++ {
				wg.Add(1)

				go func(i int, t *testing.T) {
					defer wg.Done()
					time.Sleep(time.Millisecond * time.Duration(rand.Intn(500)))

					_, err := clients[i].TellWithTimeout("square", 4*time.Second, 2)
					if err != nil {
						t.Error(err)
					}
				}(i, t)
			}
		case <-timeout:
			fmt.Println("test stopped")
			t.SkipNow()
		}

	}

	wg.Wait()
}

// Call a single method with multiple clients. This test is implemented to be
// sure the method is calling back with in the same time and not timing out.
func TestConcurrency(t *testing.T) {
	// Create a mathworker kite
	mathKite := newXhrKite("mathworker", "0.0.1")
	mathKite.Config.DisableAuthentication = true
	mathKite.HandleFunc("ping", func(r *Request) (interface{}, error) {
		time.Sleep(time.Second)
		return "pong", nil
	})
	go http.ListenAndServe("127.0.0.1:3637", mathKite)

	// Wait until it's started
	time.Sleep(time.Second)

	// number of exp kites that will call mathworker kite
	clientNumber := 10

	fmt.Printf("Creating %d exp clients\n", clientNumber)
	clients := make([]*Client, clientNumber)
	for i := 0; i < clientNumber; i++ {
		c := newXhrKite("exp", "0.0.1").NewClient("http://127.0.0.1:3637/kite")
		if err := c.Dial(); err != nil {
			t.Fatal(err)
		}

		clients[i] = c
	}

	var wg sync.WaitGroup

	for i := range clients {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			result, err := clients[i].TellWithTimeout("ping", 4*time.Second)
			if err != nil {
				t.Fatal(err)
			}

			if result.MustString() != "pong" {
				t.Errorf("Got %s want: pong", result.MustString())
			}
		}(i)
	}

	wg.Wait()
}

// Test 2 way communication between kites.
func TestKite(t *testing.T) {
	// Create a mathworker kite
	mathKite := newXhrKite("mathworker", "0.0.1")
	mathKite.Config.DisableAuthentication = true
	mathKite.HandleFunc("square", Square)
	mathKite.HandleFunc("squareCB", SquareCB)
	mathKite.HandleFunc("sleep", Sleep)
	go http.ListenAndServe("127.0.0.1:3636", mathKite)

	// Wait until it's started
	time.Sleep(time.Second)

	// Create exp2 kite
	exp2Kite := newXhrKite("exp2", "0.0.1")
	fooChan := make(chan string)
	exp2Kite.HandleFunc("foo", func(r *Request) (interface{}, error) {
		s := r.Args.One().MustString()
		t.Logf("Message received: %s\n", s)
		fooChan <- s
		return nil, nil
	})

	// exp2 connects to mathworker
	remote := exp2Kite.NewClient("http://127.0.0.1:3636/kite")
	err := remote.Dial()
	if err != nil {
		t.Fatal(err)
	}

	result, err := remote.TellWithTimeout("square", 4*time.Second, 2)
	if err != nil {
		t.Fatal(err)
	}

	number := result.MustFloat64()

	t.Logf("rpc result: %f\n", number)

	if number != 4 {
		t.Fatalf("Invalid result: %f", number)
	}

	select {
	case s := <-fooChan:
		if s != "bar" {
			t.Fatalf("Invalid message: %s", s)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("Did not get the message")
	}

	resultChan := make(chan float64, 1)
	resultCallback := func(args *dnode.Partial) {
		n := args.One().MustFloat64()
		resultChan <- n
	}

	result, err = remote.TellWithTimeout("squareCB", 4*time.Second, 3, dnode.Callback(resultCallback))
	if err != nil {
		t.Fatal(err)
	}

	select {
	case n := <-resultChan:
		if n != 9.0 {
			t.Fatalf("Unexpected result: %f", n)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("Did not get the message")
	}

	result, err = remote.TellWithTimeout("sleep", time.Second)
	if err == nil {
		t.Fatal("Did get message in 1 seconds, however the sleep method takes 2 seconds to response")
	}

	result, err = remote.Tell("sleep")
	if err != nil {
		t.Fatal(err)
	}

	if !result.MustBool() {
		t.Fatal("sleep result must be true")
	}

}

// Sleeps for 2 seconds and returns true
func Sleep(r *Request) (interface{}, error) {
	time.Sleep(time.Second * 2)
	return true, nil
}

// Returns the result. Also tests reverse call.
func Square(r *Request) (interface{}, error) {
	a := r.Args.One().MustFloat64()
	result := a * a

	r.LocalKite.Log.Info("Kite call, sending result '%f' back\n", result)

	// Reverse method call
	r.Client.Go("foo", "bar")

	return result, nil
}

// Calls the callback with the result. For testing requests with Callback.
func SquareCB(r *Request) (interface{}, error) {
	args := r.Args.MustSliceOfLength(2)
	a := args[0].MustFloat64()
	cb := args[1].MustFunction()

	result := a * a

	r.LocalKite.Log.Info("Kite call, sending result '%f' back\n", result)

	// Send the result.
	err := cb.Call(result)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func newXhrKite(name, version string) *Kite {
	k := New(name, version)
	k.Config.Transport = config.XHRPolling
	return k
}
<|endoftext|>