<commit_before>package etcd

import (
	"bytes"
	"encoding/gob"
	"net/http"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	etcdraft "github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/rafthttp"
	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
)

type tag struct {
	UID  uint64
	Data []byte
}

type future struct {
	res chan raft.Result
}

func (f *future) ResultCh() <-chan raft.Result {
	return f.res
}

// Wrapper wraps an etcd/raft.Node and implements relab/raft.Raft and
// etcd/rafthttp.Raft.
type Wrapper struct {
	n         etcdraft.Node
	sm        raft.StateMachine
	storage   *etcdraft.MemoryStorage
	transport *rafthttp.Transport
	logger    logrus.FieldLogger

	heartbeat time.Duration

	uid       uint64
	propLock  sync.RWMutex
	proposals map[uint64]chan<- raft.Result
}

func (w *Wrapper) encodeProposal(data []byte) (uint64, []byte, error) {
	uid := atomic.AddUint64(&w.uid, 1)
	t := &tag{
		UID:  uid,
		Data: data,
	}

	var b bytes.Buffer
	enc := gob.NewEncoder(&b)
	err := enc.Encode(t)
	if err != nil {
		return 0, nil, err
	}

	return uid, b.Bytes(), nil
}

func (w *Wrapper) newFuture(uid uint64) raft.Future {
	res := make(chan raft.Result, 1)
	future := &future{res}

	w.propLock.Lock()
	w.proposals[uid] = res
	w.propLock.Unlock()

	return future
}

func (w *Wrapper) deleteFuture(uid uint64) {
	w.propLock.Lock()
	delete(w.proposals, uid)
	w.propLock.Unlock()
}

// Hack: Returns nil if commit fails to decode. This when etcd append the
// initial configuration to the log. Fix: Make state machine return
// clientID/clientSeq along with result, we can then use that to figure out who
// to respond to instead of using the tag struct. Etcd uses the same hack
// internally so we'll just leave it be for now.
func (w *Wrapper) decodeCommit(commit []byte) *tag {
	b := bytes.NewBuffer(commit)
	dec := gob.NewDecoder(b)

	var t tag
	err := dec.Decode(&t)

	if err != nil {
		return nil
	}

	return &t
}

func NewRaft(logger logrus.FieldLogger,
	sm raft.StateMachine, storage *etcdraft.MemoryStorage, cfg *etcdraft.Config,
	peers []etcdraft.Peer, heartbeat time.Duration,
) *Wrapper {
	w := &Wrapper{
		sm:        sm,
		storage:   storage,
		heartbeat: heartbeat,
		proposals: make(map[uint64]chan<- raft.Result),
		logger:    logger,
	}
	w.n = etcdraft.StartNode(cfg, append(peers, etcdraft.Peer{ID: cfg.ID}))

	ss := &stats.ServerStats{}
	ss.Initialize()

	transport := &rafthttp.Transport{
		ID:          types.ID(cfg.ID),
		ClusterID:   1,
		Raft:        w,
		ServerStats: ss,
		LeaderStats: stats.NewLeaderStats(strconv.FormatUint(cfg.ID, 10)),
		ErrorC:      make(chan error),
	}
	err := transport.Start()

	if err != nil {
		panic("start transport: " + err.Error())
	}

	for i, peer := range peers {
		w.logger.WithFields(logrus.Fields{
			"i":       i,
			"id":      peer.ID,
			"context": string(peer.Context),
		}).Warnln("Add peer")

		transport.AddPeer(types.ID(peer.ID), []string{string(peer.Context)})
	}

	w.transport = transport

	go w.run()
	return w
}

func (w *Wrapper) Handler() http.Handler {
	return w.transport.Handler()
}

func (w *Wrapper) ProposeCmd(ctx context.Context, req []byte) (raft.Future, error) {
	uid, prop, err := w.encodeProposal(req)

	if err != nil {
		return nil, err
	}

	future := w.newFuture(uid)
	err = w.n.Propose(ctx, prop)

	if err != nil {
		delete(w.proposals, uid)
		return nil, err
	}

	return future, nil
}

func (w *Wrapper) ReadCmd(context.Context, []byte) (raft.Future, error) {
	w.logger.Warnln("ProposeRead")
	return nil, nil
}

func (w *Wrapper) ProposeConf(ctx context.Context, req *commonpb.ReconfRequest) (raft.Future, error) {
	cc := raftpb.ConfChange{
		Type:    raftpb.ConfChangeType(req.ReconfType),
		ID:      req.ServerID, // ?
		NodeID:  req.ServerID,
		Context: []byte(""), // ?
	}

	err := w.n.ProposeConfChange(ctx, cc)

	w.logger.WithError(err).WithFields(logrus.Fields{
		"req": req,
		"cc":  cc,
	}).Warnln("ProposeConf")

	if err != nil {
		return nil, err
	}

	panic("ProposeConf not implemented")
}

func (w *Wrapper) run() {
	s := time.NewTicker(w.heartbeat)

	for {
		select {
		case <-s.C:
			w.n.Tick()
		case rd := <-w.n.Ready():
			if rd.SoftState != nil {
				rmetrics.leader.Set(float64(rd.Lead))
				if rd.RaftState != etcdraft.StateLeader && len(w.proposals) > 0 {
					w.propLock.Lock()
					for uid, respCh := range w.proposals {
						respCh <- raft.Result{
							Value: raft.ErrNotLeader{Leader: rd.Lead},
						}
						delete(w.proposals, uid)
					}
					w.propLock.Unlock()
				}
			}
			w.storage.Append(rd.Entries)
			if !etcdraft.IsEmptyHardState(rd.HardState) {
				w.storage.SetHardState(rd.HardState)
			}
			if !etcdraft.IsEmptySnap(rd.Snapshot) {
				w.storage.ApplySnapshot(rd.Snapshot)
			}
			w.transport.Send(rd.Messages)
			for _, entry := range rd.CommittedEntries {
				rmetrics.commitIndex.Set(float64(entry.Index))

				t := w.decodeCommit(entry.Data)

				switch entry.Type {
				case raftpb.EntryNormal:
					w.handleNormal(&entry, t)
				case raftpb.EntryConfChange:
					w.logger.WithField(
						"entry", etcdraft.DescribeEntry(entry, nil),
					).Warnln("Committed conf change entry")

					w.handleConfChange(&entry, t)
				}

				// TODO Respond to future.
			}
			w.n.Advance()
		}
	}
}

func (w *Wrapper) handleNormal(entry *raftpb.Entry, t *tag) {
	if t == nil {
		return
	}

	res := w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryNormal,
		Data:      t.Data,
	})

	w.propLock.RLock()
	respCh, ok := w.proposals[t.UID]
	w.propLock.RUnlock()

	if ok {
		respCh <- raft.Result{
			Index: entry.Index,
			Value: res,
		}
		w.deleteFuture(t.UID)
	}
}

func (w *Wrapper) handleConfChange(entry *raftpb.Entry, t *tag) {
	var cc raftpb.ConfChange
	err := cc.Unmarshal(entry.Data)

	if err != nil {
		panic("unmarshal conf change: " + err.Error())
	}

	w.logger.WithField("cc", cc).Warnln("Applying conf change")
	w.n.ApplyConfChange(cc)
	switch cc.Type {
	case raftpb.ConfChangeAddNode:
		if len(cc.Context) > 0 {
			w.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
		}
	case raftpb.ConfChangeRemoveNode:
		tid := types.ID(cc.NodeID)
		if tid == w.transport.ID {
			w.logger.Warnln("Shutting down")
			return
		}
		w.transport.RemovePeer(tid)
	}

	if t == nil {
		return
	}

	// Inform state machine about new configuration.
	w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryReconf,
		Data:      t.Data,
	})

	// TODO Respond / Cleanup future.
}

func (w *Wrapper) Process(ctx context.Context, m raftpb.Message) error {
	return w.n.Step(ctx, m)
}
func (w *Wrapper) IsIDRemoved(id uint64) bool                               { return false }
func (w *Wrapper) ReportUnreachable(id uint64)                              {}
func (w *Wrapper) ReportSnapshot(id uint64, status etcdraft.SnapshotStatus) {}
<commit_msg>etcd/raft.go: Apply entries to the state machine asynchronously<commit_after>package etcd

import (
	"bytes"
	"encoding/gob"
	"net/http"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"

	"github.com/Sirupsen/logrus"
	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	etcdraft "github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/rafthttp"
	"github.com/relab/raft"
	"github.com/relab/raft/commonpb"
)

type tag struct {
	UID  uint64
	Data []byte
}

type future struct {
	res chan raft.Result
}

func (f *future) ResultCh() <-chan raft.Result {
	return f.res
}

// Wrapper wraps an etcd/raft.Node and implements relab/raft.Raft and
// etcd/rafthttp.Raft.
type Wrapper struct {
	n         etcdraft.Node
	sm        raft.StateMachine
	storage   *etcdraft.MemoryStorage
	transport *rafthttp.Transport
	logger    logrus.FieldLogger

	heartbeat time.Duration

	uid       uint64
	propLock  sync.Mutex
	proposals map[uint64]chan<- raft.Result

	apply chan []raftpb.Entry
}

func (w *Wrapper) encodeProposal(data []byte) (uint64, []byte, error) {
	uid := atomic.AddUint64(&w.uid, 1)
	t := &tag{
		UID:  uid,
		Data: data,
	}

	var b bytes.Buffer
	enc := gob.NewEncoder(&b)
	err := enc.Encode(t)
	if err != nil {
		return 0, nil, err
	}

	return uid, b.Bytes(), nil
}

func (w *Wrapper) newFuture(uid uint64) raft.Future {
	res := make(chan raft.Result, 1)
	future := &future{res}

	w.propLock.Lock()
	w.proposals[uid] = res
	w.propLock.Unlock()

	return future
}

// Hack: Returns nil if commit fails to decode. This when etcd append the
// initial configuration to the log. Fix: Make state machine return
// clientID/clientSeq along with result, we can then use that to figure out who
// to respond to instead of using the tag struct. Etcd uses the same hack
// internally so we'll just leave it be for now.
func (w *Wrapper) decodeCommit(commit []byte) *tag {
	b := bytes.NewBuffer(commit)
	dec := gob.NewDecoder(b)

	var t tag
	err := dec.Decode(&t)

	if err != nil {
		return nil
	}

	return &t
}

func NewRaft(logger logrus.FieldLogger,
	sm raft.StateMachine, storage *etcdraft.MemoryStorage, cfg *etcdraft.Config,
	peers []etcdraft.Peer, heartbeat time.Duration,
) *Wrapper {
	w := &Wrapper{
		sm:        sm,
		storage:   storage,
		heartbeat: heartbeat,
		proposals: make(map[uint64]chan<- raft.Result),
		logger:    logger,
		apply:     make(chan []raftpb.Entry, 2048),
	}
	w.n = etcdraft.StartNode(cfg, append(peers, etcdraft.Peer{ID: cfg.ID}))

	ss := &stats.ServerStats{}
	ss.Initialize()

	transport := &rafthttp.Transport{
		ID:          types.ID(cfg.ID),
		ClusterID:   1,
		Raft:        w,
		ServerStats: ss,
		LeaderStats: stats.NewLeaderStats(strconv.FormatUint(cfg.ID, 10)),
		ErrorC:      make(chan error),
	}
	err := transport.Start()

	if err != nil {
		panic("start transport: " + err.Error())
	}

	for i, peer := range peers {
		w.logger.WithFields(logrus.Fields{
			"i":       i,
			"id":      peer.ID,
			"context": string(peer.Context),
		}).Warnln("Add peer")

		transport.AddPeer(types.ID(peer.ID), []string{string(peer.Context)})
	}

	w.transport = transport

	go w.runSM()
	go w.run()
	return w
}

func (w *Wrapper) Handler() http.Handler {
	return w.transport.Handler()
}

func (w *Wrapper) ProposeCmd(ctx context.Context, req []byte) (raft.Future, error) {
	uid, prop, err := w.encodeProposal(req)

	if err != nil {
		return nil, err
	}

	future := w.newFuture(uid)
	err = w.n.Propose(ctx, prop)

	if err != nil {
		delete(w.proposals, uid)
		return nil, err
	}

	return future, nil
}

func (w *Wrapper) ReadCmd(context.Context, []byte) (raft.Future, error) {
	w.logger.Warnln("ProposeRead")
	return nil, nil
}

func (w *Wrapper) ProposeConf(ctx context.Context, req *commonpb.ReconfRequest) (raft.Future, error) {
	cc := raftpb.ConfChange{
		Type:    raftpb.ConfChangeType(req.ReconfType),
		ID:      req.ServerID, // ?
		NodeID:  req.ServerID,
		Context: []byte(""), // ?
	}

	err := w.n.ProposeConfChange(ctx, cc)

	w.logger.WithError(err).WithFields(logrus.Fields{
		"req": req,
		"cc":  cc,
	}).Warnln("ProposeConf")

	if err != nil {
		return nil, err
	}

	panic("ProposeConf not implemented")
}

func (w *Wrapper) run() {
	s := time.NewTicker(w.heartbeat)

	for {
		select {
		case <-s.C:
			w.n.Tick()
		case rd := <-w.n.Ready():
			if rd.SoftState != nil {
				rmetrics.leader.Set(float64(rd.Lead))
				if rd.RaftState != etcdraft.StateLeader && len(w.proposals) > 0 {
					w.propLock.Lock()
					for uid, respCh := range w.proposals {
						respCh <- raft.Result{
							Value: raft.ErrNotLeader{Leader: rd.Lead},
						}
						delete(w.proposals, uid)
					}
					w.propLock.Unlock()
				}
			}
			w.storage.Append(rd.Entries)
			if !etcdraft.IsEmptyHardState(rd.HardState) {
				w.storage.SetHardState(rd.HardState)
			}
			if !etcdraft.IsEmptySnap(rd.Snapshot) {
				w.storage.ApplySnapshot(rd.Snapshot)
			}
			w.n.Advance()
			w.transport.Send(rd.Messages)
			w.apply <- rd.CommittedEntries
		}
	}
}

func (w *Wrapper) runSM() {
	for entries := range w.apply {
		tags := make(map[uint64]*tag)

		for _, entry := range entries {
			tags[entry.Index] = w.decodeCommit(entry.Data)
		}

		chs := make(map[uint64]chan<- raft.Result)

		w.propLock.Lock()
		for index, tag := range tags {
			if tag == nil {
				continue
			}
			chs[index] = w.proposals[tag.UID]
		}
		w.propLock.Unlock()

		for _, entry := range entries {
			rmetrics.commitIndex.Set(float64(entry.Index))

			t := tags[entry.Index]
			ch := chs[entry.Index]

			switch entry.Type {
			case raftpb.EntryNormal:
				w.handleNormal(&entry, t, ch)
			case raftpb.EntryConfChange:
				w.logger.WithField(
					"entry", etcdraft.DescribeEntry(entry, nil),
				).Warnln("Committed conf change entry")

				w.handleConfChange(&entry, t, ch)
			}

			// TODO Respond to future.
		}

		w.propLock.Lock()
		for _, tag := range tags {
			if tag == nil {
				continue
			}
			delete(w.proposals, tag.UID)
		}
		w.propLock.Unlock()
	}
}

func (w *Wrapper) handleNormal(entry *raftpb.Entry, t *tag, respCh chan<- raft.Result) {
	if t == nil {
		return
	}

	res := w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryNormal,
		Data:      t.Data,
	})

	if respCh == nil {
		return
	}

	respCh <- raft.Result{
		Index: entry.Index,
		Value: res,
	}
}

func (w *Wrapper) handleConfChange(entry *raftpb.Entry, t *tag, respCh chan<- raft.Result) {
	var cc raftpb.ConfChange
	err := cc.Unmarshal(entry.Data)

	if err != nil {
		panic("unmarshal conf change: " + err.Error())
	}

	w.logger.WithField("cc", cc).Warnln("Applying conf change")
	w.n.ApplyConfChange(cc)
	switch cc.Type {
	case raftpb.ConfChangeAddNode:
		if len(cc.Context) > 0 {
			w.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
		}
	case raftpb.ConfChangeRemoveNode:
		tid := types.ID(cc.NodeID)
		if tid == w.transport.ID {
			w.logger.Warnln("Shutting down")
			return
		}
		w.transport.RemovePeer(tid)
	}

	if t == nil {
		return
	}

	// Inform state machine about new configuration.
	w.sm.Apply(&commonpb.Entry{
		Term:      entry.Term,
		Index:     entry.Index,
		EntryType: commonpb.EntryReconf,
		Data:      t.Data,
	})

	// TODO Respond / Cleanup future.
}

func (w *Wrapper) Process(ctx context.Context, m raftpb.Message) error {
	return w.n.Step(ctx, m)
}
func (w *Wrapper) IsIDRemoved(id uint64) bool                               { return false }
func (w *Wrapper) ReportUnreachable(id uint64)                              {}
func (w *Wrapper) ReportSnapshot(id uint64, status etcdraft.SnapshotStatus) {}
<|endoftext|>


<commit_before>package eval

import (
	"fmt"
	"jacob/black/ast"
	"jacob/black/object"
	"jacob/black/token"
	"math"
)

var (
	// ConstTrue is the only and only true value
	ConstTrue = &object.Boolean{Value: true}
	// ConstFalse is the only and only false value
	ConstFalse = &object.Boolean{Value: false}
	// ConstNil is the only and only nil
	ConstNil = &object.Nil{}
)

func newError(pos token.Position, format string, v ...interface{}) *object.Error {
	return &object.Error{Message: fmt.Sprintf(format, v...), Pos: pos}
}

func isError(o object.Object) bool {
	if o != nil {
		return o.Type() == object.ErrorType
	}
	return false
}

// Eval evaluates the program node and returns an object as a result
func Eval(node ast.Node, env *object.Environment) object.Object {
	switch node := node.(type) {

	// statements
	case *ast.Program:
		return evalProgram(node, env)
	case *ast.LetStatement:
		val := Eval(node.Value, env)
		if isError(val) {
			return val
		}
		env.Set(node.Name.Value, val)
	case *ast.ReturnStatement:
		val := Eval(node.Value, env)
		if isError(val) {
			return val
		}
		return &object.ReturnValue{Value: val}
	case *ast.BlockStatement:
		return evalBlockStatement(node, env)
	case *ast.ExpressionStatement:
		return Eval(node.Expression, env)

		// expressions
	case *ast.PrefixExpression:
		right := Eval(node.Right, env)
		if isError(right) {
			return right
		}
		return evalPrefixExpr(node.Token, right)
	case *ast.InfixExpression:

		if node.Operator == token.Assign {
			if v := evalAssign(node, env); v != nil {
				return v
			}
			return newError(node.Token.Pos, "cannot bind a literal to a value")
		}

		left := Eval(node.Left, env)
		if isError(left) {
			return left
		}
		right := Eval(node.Right, env)
		if isError(right) {
			return right
		}
		return evalInfixExpr(node.Token, left, right)
	case *ast.IfExpression:
		return evalIfExpr(node, env)
	case *ast.CallExpression:
		function := Eval(node.Func, env)
		if isError(function) {
			return function
		}

		args, err := evalExpressions(node.Args, env)
		if err != nil {
			return err
		}

		return doFunction(node.Token.Pos, function, args)

		// literals
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	case *ast.FloatLiteral:
		return &object.Float{Value: node.Value}
	case *ast.BooleanLiteral:
		return boolToBoolean(node.Value)
	case *ast.FunctionLiteral:
		return &object.Function{Params: node.Params, Body: node.Body, Env: env}

	case *ast.Identifier:
		return evalIdentifier(node, env)
	}
	return nil
}

func evalProgram(program *ast.Program, env *object.Environment) object.Object {
	var result object.Object

	for _, s := range program.Statements {
		result = Eval(s, env)

		if result != nil {
			// pass up the return type to the top level
			switch result.Type() {
			case object.ReturnType:
				// unwrap the return value
				return result.(*object.ReturnValue).Value
			case object.ErrorType:
				return result.(*object.Error)
			}
		}
	}

	return result
}

func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object {
	var result object.Object

	for _, s := range block.Statements {
		result = Eval(s, env)

		if result != nil {
			if result.Type() == object.ReturnType || result.Type() == object.ErrorType {
				return result
			}
		}
	}

	return result
}

func evalExpressions(expressions []ast.Expression, env *object.Environment) ([]object.Object, object.Object) {
	var evaluated []object.Object

	for _, e := range expressions {
		evaled := Eval(e, env)
		if isError(evaled) {
			return []object.Object{}, evaled
		}
		evaluated = append(evaluated, evaled)
	}

	return evaluated, nil
}

func evalIfExpr(node *ast.IfExpression, env *object.Environment) object.Object {
	cond := Eval(node.Cond, env)

	if isError(cond) {
		return cond
	}

	if isTruthy(cond) {
		return Eval(node.Do, env)
	} else if node.Else != nil {
		return Eval(node.Else, env)
	}

	return ConstNil
}

// isTruthy - everything is true execpt for false and nil
func isTruthy(o object.Object) bool {
	switch o {
	case ConstFalse, ConstNil:
		return false
	default:
		// special case: 0 or 0.0 is not truthy
		switch o.Type() {
		case object.IntType:
			if o.(*object.Integer).Value == 0 {
				return false
			}
		case object.FloatType:
			if o.(*object.Float).Value == 0.0 {
				return false
			}
		}

		return true
	}
}

func boolToBoolean(b bool) *object.Boolean {
	if b {
		return ConstTrue
	}
	return ConstFalse
}

func evalPrefixExpr(op token.Token, right object.Object) object.Object {
	switch op.Type {
	case token.Bang:
		return evalBangOperatorExpr(right)
	case token.Minus:
		return evalMinusPrefixOperatorExpr(op.Pos, right)
	default:
		return newError(op.Pos, "unknown operator '%s' for type '%s'", op, right.Type())
	}
}

func evalInfixExpr(op token.Token, left object.Object, right object.Object) object.Object {

	if !left.CanApply(op.Type, right.Type()) {
		return newError(op.Pos, "cannot apply operator '%s' for type '%s' and '%s'", op, left.Type(), right.Type())
	}

	// test and convert int to float if needed
	if left.Type() == object.IntType && right.Type() == object.IntType {
		// both ints. easy
		return evalIntegerInfixExpr(op, left, right)
	} else if left.Type() == object.FloatType && right.Type() == object.FloatType {
		// both floats. easy
		return evalFloatInfixExpr(op, left, right)
	}

	// one of them must be a int and the other a float
	if left.Type() == object.IntType || right.Type() == object.IntType {
		if left.Type() == object.FloatType {
			// left is float, right is int.
			// premote right to float
			val := right.(*object.Integer).Value
			right = &object.Float{Value: float64(val)}
			return evalFloatInfixExpr(op, left, right)

		} else if right.Type() == object.FloatType {
			// right is float, left is int
			// premote left to float
			val := left.(*object.Integer).Value
			left = &object.Float{Value: float64(val)}

			return evalFloatInfixExpr(op, left, right)
		}
	}

	// compare actual runtime object
	if op.Type == token.Equal {
		return boolToBoolean(left == right)
	} else if op.Type == token.NotEqual {
		return boolToBoolean(left != right)
	}

	// otherwise 2 objects that don't match
	return newError(op.Pos, "unknown operator '%s' for type '%s' and '%s'", op, left.Type(), right.Type())
}

func evalIntegerInfixExpr(op token.Token, left object.Object, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value

	switch op.Type {
	case token.Plus:
		return &object.Integer{Value: leftVal + rightVal}
	case token.Minus:
		return &object.Integer{Value: leftVal - rightVal}
	case token.Times:
		return &object.Integer{Value: leftVal * rightVal}
	case token.Divide:
		if rightVal == 0 {
			return newError(op.Pos, "cannot divide %d by 0", leftVal)
		}
		return &object.Integer{Value: leftVal / rightVal}
	case token.Exp:
		return &object.Integer{Value: int64(math.Pow(float64(leftVal), float64(rightVal)))}
	case token.Mod:
		if rightVal == 0 {
			return newError(op.Pos, "cannot modulo %d by 0", leftVal)
		}
		return &object.Integer{Value: leftVal % rightVal}
	case token.Less:
		return boolToBoolean(leftVal < rightVal)
	case token.Greater:
		return boolToBoolean(leftVal > rightVal)
	case token.Equal:
		return boolToBoolean(leftVal == rightVal)
	case token.NotEqual:
		return boolToBoolean(leftVal != rightVal)
	default:
		return newError(op.Pos, "unknown operator '%s' for type '%s' and '%s'", op.Type, left.Type(), right.Type())
	}
}

func evalFloatInfixExpr(op token.Token, left object.Object, right object.Object) object.Object {
	leftVal := left.(*object.Float).Value
	rightVal := right.(*object.Float).Value

	switch op.Type {
	case token.Plus:
		return &object.Float{Value: leftVal + rightVal}
	case token.Minus:
		return &object.Float{Value: leftVal - rightVal}
	case token.Times:
		return &object.Float{Value: leftVal * rightVal}
	case token.Divide:
		if rightVal == 0 {
			return newError(op.Pos, "cannot divide %d by 0", leftVal)
		}
		return &object.Float{Value: leftVal / rightVal}
	case token.Exp:
		return &object.Float{Value: math.Pow(leftVal, rightVal)}
	case token.Mod:
		if rightVal == 0 {
			return newError(op.Pos, "cannot modulo %d by 0", leftVal)
		}
		return &object.Float{Value: math.Mod(leftVal, rightVal)}
	case token.Less:
		return boolToBoolean(leftVal < rightVal)
	case token.Greater:
		return boolToBoolean(leftVal > rightVal)
	case token.Equal:
		return boolToBoolean(leftVal == rightVal)
	case token.NotEqual:
		return boolToBoolean(leftVal != rightVal)
	default:
		return newError(op.Pos, "unknown operator '%s' for type '%s' and '%s'", op.Type, left.Type(), right.Type())
	}
}

func evalBangOperatorExpr(right object.Object) object.Object {
	switch right {
	case ConstTrue:
		return ConstFalse
	case ConstFalse:
		return ConstTrue
	case ConstNil:
		return ConstTrue
	default:
		return boolToBoolean(!isTruthy(right))
	}
}

func evalMinusPrefixOperatorExpr(pos token.Position, right object.Object) object.Object {
	switch right.Type() {
	case object.IntType:
		v := right.(*object.Integer).Value
		return &object.Integer{Value: -v}
	case object.FloatType:
		v := right.(*object.Float).Value
		return &object.Float{Value: -v}
	default:
		return newError(pos, "unknown operator '-' for type '%s'", right.Type())
	}
}

func evalIdentifier(id *ast.Identifier, env *object.Environment) object.Object {
	if val, ok := env.Get(id.Value); ok {
		return val
	}
	return newError(id.Token.Pos, "identifier not found: %s", id.Value)
}

func evalAssign(node *ast.InfixExpression, env *object.Environment) object.Object {
	// special case = assign operator
	switch l := node.Left.(type) {
	case *ast.Identifier:
		// check if exists already
		if val, ok := env.Get(l.Value); ok {
			if isError(val) {
				return val
			}

			// eval rhs
			right := Eval(node.Right, env)
			if isError(right) {
				return right
			}

			// must be same type
			if val.Type() == right.Type() {
				env.Set(l.Value, right)
				return right
			}

			return newError(l.Token.Pos, "cannot assign variable '%s' of type '%s' to value '%s' of type '%s'", l.Value, val.Type(), right, right.Type())
		}
		return newError(l.Token.Pos, "cannot assign value to variable '%s' that does not exist", l.Value)
	default:
		return nil
	}
}

func doFunction(pos token.Position, f object.Object, args []object.Object) object.Object {
	function, ok := f.(*object.Function)
	if !ok {
		return newError(pos, "'%s' is not a function", f.Type())
	}

	childEnv := adoptFunctionEnv(function, args)
	evaluated := Eval(function.Body, childEnv)

	if val, ok := evaluated.(*object.ReturnValue); ok {
		return val.Value
	}

	return evaluated
}

func adoptFunctionEnv(f *object.Function, args []object.Object) *object.Environment {
	env := object.NewChildEnvironment(f.Env)

	for i, p := range f.Params {
		env.Set(p.Value, args[i])
	}

	return env
}
<commit_msg>runtime args error<commit_after>package eval

import (
	"fmt"
	"jacob/black/ast"
	"jacob/black/object"
	"jacob/black/token"
	"math"
)

var (
	// ConstTrue is the only and only true value
	ConstTrue = &object.Boolean{Value: true}
	// ConstFalse is the only and only false value
	ConstFalse = &object.Boolean{Value: false}
	// ConstNil is the only and only nil
	ConstNil = &object.Nil{}
)

func newError(pos token.Position, format string, v ...interface{}) *object.Error {
	return &object.Error{Message: fmt.Sprintf(format, v...), Pos: pos}
}

func isError(o object.Object) bool {
	if o != nil {
		return o.Type() == object.ErrorType
	}
	return false
}

// Eval evaluates the program node and returns an object as a result
func Eval(node ast.Node, env *object.Environment) object.Object {
	switch node := node.(type) {

	// statements
	case *ast.Program:
		return evalProgram(node, env)
	case *ast.LetStatement:
		val := Eval(node.Value, env)
		if isError(val) {
			return val
		}
		env.Set(node.Name.Value, val)
	case *ast.ReturnStatement:
		val := Eval(node.Value, env)
		if isError(val) {
			return val
		}
		return &object.ReturnValue{Value: val}
	case *ast.BlockStatement:
		return evalBlockStatement(node, env)
	case *ast.ExpressionStatement:
		return Eval(node.Expression, env)

		// expressions
	case *ast.PrefixExpression:
		right := Eval(node.Right, env)
		if isError(right) {
			return right
		}
		return evalPrefixExpr(node.Token, right)
	case *ast.InfixExpression:

		if node.Operator == token.Assign {
			if v := evalAssign(node, env); v != nil {
				return v
			}
			return newError(node.Token.Pos, "cannot bind a literal to a value")
		}

		left := Eval(node.Left, env)
		if isError(left) {
			return left
		}
		right := Eval(node.Right, env)
		if isError(right) {
			return right
		}
		return evalInfixExpr(node.Token, left, right)
	case *ast.IfExpression:
		return evalIfExpr(node, env)
	case *ast.CallExpression:
		function := Eval(node.Func, env)
		if isError(function) {
			return function
		}

		args, err := evalExpressions(node.Args, env)
		if err != nil {
			return err
		}

		return doFunction(node.Token, function, args)

		// literals
	case *ast.IntegerLiteral:
		return &object.Integer{Value: node.Value}
	case *ast.FloatLiteral:
		return &object.Float{Value: node.Value}
	case *ast.BooleanLiteral:
		return boolToBoolean(node.Value)
	case *ast.FunctionLiteral:
		return &object.Function{Params: node.Params, Body: node.Body, Env: env}

	case *ast.Identifier:
		return evalIdentifier(node, env)
	}
	return nil
}

func evalProgram(program *ast.Program, env *object.Environment) object.Object {
	var result object.Object

	for _, s := range program.Statements {
		result = Eval(s, env)

		if result != nil {
			// pass up the return type to the top level
			switch result.Type() {
			case object.ReturnType:
				// unwrap the return value
				return result.(*object.ReturnValue).Value
			case object.ErrorType:
				return result.(*object.Error)
			}
		}
	}

	return result
}

func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object {
	var result object.Object

	for _, s := range block.Statements {
		result = Eval(s, env)

		if result != nil {
			if result.Type() == object.ReturnType || result.Type() == object.ErrorType {
				return result
			}
		}
	}

	return result
}

func evalExpressions(expressions []ast.Expression, env *object.Environment) ([]object.Object, object.Object) {
	var evaluated []object.Object

	for _, e := range expressions {
		evaled := Eval(e, env)
		if isError(evaled) {
			return []object.Object{}, evaled
		}
		evaluated = append(evaluated, evaled)
	}

	return evaluated, nil
}

func evalIfExpr(node *ast.IfExpression, env *object.Environment) object.Object {
	cond := Eval(node.Cond, env)

	if isError(cond) {
		return cond
	}

	if isTruthy(cond) {
		return Eval(node.Do, env)
	} else if node.Else != nil {
		return Eval(node.Else, env)
	}

	return ConstNil
}

// isTruthy - everything is true execpt for false and nil
func isTruthy(o object.Object) bool {
	switch o {
	case ConstFalse, ConstNil:
		return false
	default:
		// special case: 0 or 0.0 is not truthy
		switch o.Type() {
		case object.IntType:
			if o.(*object.Integer).Value == 0 {
				return false
			}
		case object.FloatType:
			if o.(*object.Float).Value == 0.0 {
				return false
			}
		}

		return true
	}
}

func boolToBoolean(b bool) *object.Boolean {
	if b {
		return ConstTrue
	}
	return ConstFalse
}

func evalPrefixExpr(op token.Token, right object.Object) object.Object {
	switch op.Type {
	case token.Bang:
		return evalBangOperatorExpr(right)
	case token.Minus:
		return evalMinusPrefixOperatorExpr(op.Pos, right)
	default:
		return newError(op.Pos, "unknown operator '%s' for type '%s'", op, right.Type())
	}
}

func evalInfixExpr(op token.Token, left object.Object, right object.Object) object.Object {

	if !left.CanApply(op.Type, right.Type()) {
		return newError(op.Pos, "cannot apply operator '%s' for type '%s' and '%s'", op, left.Type(), right.Type())
	}

	// test and convert int to float if needed
	if left.Type() == object.IntType && right.Type() == object.IntType {
		// both ints. easy
		return evalIntegerInfixExpr(op, left, right)
	} else if left.Type() == object.FloatType && right.Type() == object.FloatType {
		// both floats. easy
		return evalFloatInfixExpr(op, left, right)
	}

	// one of them must be a int and the other a float
	if left.Type() == object.IntType || right.Type() == object.IntType {
		if left.Type() == object.FloatType {
			// left is float, right is int.
			// premote right to float
			val := right.(*object.Integer).Value
			right = &object.Float{Value: float64(val)}
			return evalFloatInfixExpr(op, left, right)

		} else if right.Type() == object.FloatType {
			// right is float, left is int
			// premote left to float
			val := left.(*object.Integer).Value
			left = &object.Float{Value: float64(val)}

			return evalFloatInfixExpr(op, left, right)
		}
	}

	// compare actual runtime object
	if op.Type == token.Equal {
		return boolToBoolean(left == right)
	} else if op.Type == token.NotEqual {
		return boolToBoolean(left != right)
	}

	// otherwise 2 objects that don't match
	return newError(op.Pos, "unknown operator '%s' for type '%s' and '%s'", op, left.Type(), right.Type())
}

func evalIntegerInfixExpr(op token.Token, left object.Object, right object.Object) object.Object {
	leftVal := left.(*object.Integer).Value
	rightVal := right.(*object.Integer).Value

	switch op.Type {
	case token.Plus:
		return &object.Integer{Value: leftVal + rightVal}
	case token.Minus:
		return &object.Integer{Value: leftVal - rightVal}
	case token.Times:
		return &object.Integer{Value: leftVal * rightVal}
	case token.Divide:
		if rightVal == 0 {
			return newError(op.Pos, "cannot divide %d by 0", leftVal)
		}
		return &object.Integer{Value: leftVal / rightVal}
	case token.Exp:
		return &object.Integer{Value: int64(math.Pow(float64(leftVal), float64(rightVal)))}
	case token.Mod:
		if rightVal == 0 {
			return newError(op.Pos, "cannot modulo %d by 0", leftVal)
		}
		return &object.Integer{Value: leftVal % rightVal}
	case token.Less:
		return boolToBoolean(leftVal < rightVal)
	case token.Greater:
		return boolToBoolean(leftVal > rightVal)
	case token.Equal:
		return boolToBoolean(leftVal == rightVal)
	case token.NotEqual:
		return boolToBoolean(leftVal != rightVal)
	default:
		return newError(op.Pos, "unknown operator '%s' for type '%s' and '%s'", op.Type, left.Type(), right.Type())
	}
}

func evalFloatInfixExpr(op token.Token, left object.Object, right object.Object) object.Object {
	leftVal := left.(*object.Float).Value
	rightVal := right.(*object.Float).Value

	switch op.Type {
	case token.Plus:
		return &object.Float{Value: leftVal + rightVal}
	case token.Minus:
		return &object.Float{Value: leftVal - rightVal}
	case token.Times:
		return &object.Float{Value: leftVal * rightVal}
	case token.Divide:
		if rightVal == 0 {
			return newError(op.Pos, "cannot divide %d by 0", leftVal)
		}
		return &object.Float{Value: leftVal / rightVal}
	case token.Exp:
		return &object.Float{Value: math.Pow(leftVal, rightVal)}
	case token.Mod:
		if rightVal == 0 {
			return newError(op.Pos, "cannot modulo %d by 0", leftVal)
		}
		return &object.Float{Value: math.Mod(leftVal, rightVal)}
	case token.Less:
		return boolToBoolean(leftVal < rightVal)
	case token.Greater:
		return boolToBoolean(leftVal > rightVal)
	case token.Equal:
		return boolToBoolean(leftVal == rightVal)
	case token.NotEqual:
		return boolToBoolean(leftVal != rightVal)
	default:
		return newError(op.Pos, "unknown operator '%s' for type '%s' and '%s'", op.Type, left.Type(), right.Type())
	}
}

func evalBangOperatorExpr(right object.Object) object.Object {
	switch right {
	case ConstTrue:
		return ConstFalse
	case ConstFalse:
		return ConstTrue
	case ConstNil:
		return ConstTrue
	default:
		return boolToBoolean(!isTruthy(right))
	}
}

func evalMinusPrefixOperatorExpr(pos token.Position, right object.Object) object.Object {
	switch right.Type() {
	case object.IntType:
		v := right.(*object.Integer).Value
		return &object.Integer{Value: -v}
	case object.FloatType:
		v := right.(*object.Float).Value
		return &object.Float{Value: -v}
	default:
		return newError(pos, "unknown operator '-' for type '%s'", right.Type())
	}
}

func evalIdentifier(id *ast.Identifier, env *object.Environment) object.Object {
	if val, ok := env.Get(id.Value); ok {
		return val
	}
	return newError(id.Token.Pos, "identifier not found: %s", id.Value)
}

func evalAssign(node *ast.InfixExpression, env *object.Environment) object.Object {
	// special case = assign operator
	switch l := node.Left.(type) {
	case *ast.Identifier:
		// check if exists already
		if val, ok := env.Get(l.Value); ok {
			if isError(val) {
				return val
			}

			// eval rhs
			right := Eval(node.Right, env)
			if isError(right) {
				return right
			}

			// must be same type
			if val.Type() == right.Type() {
				env.Set(l.Value, right)
				return right
			}

			return newError(l.Token.Pos, "cannot assign variable '%s' of type '%s' to value '%s' of type '%s'", l.Value, val.Type(), right, right.Type())
		}
		return newError(l.Token.Pos, "cannot assign value to variable '%s' that does not exist", l.Value)
	default:
		return nil
	}
}

func doFunction(t token.Token, f object.Object, args []object.Object) object.Object {
	function, ok := f.(*object.Function)
	if !ok {
		return newError(t.Pos, "type '%s' not a function", f.Type())
	}

	if len(function.Params) != len(args) {
		return newError(t.Pos, "invalid number of arguments for function. Expected %d got %d", len(function.Params), len(args))
	}

	childEnv := adoptFunctionEnv(function, args)
	evaluated := Eval(function.Body, childEnv)

	if val, ok := evaluated.(*object.ReturnValue); ok {
		return val.Value
	}

	return evaluated
}

func adoptFunctionEnv(f *object.Function, args []object.Object) *object.Environment {
	env := object.NewChildEnvironment(f.Env)

	for i, p := range f.Params {
		env.Set(p.Value, args[i])
	}

	return env
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/JustinBeckwith/go-yelp/yelp"
//	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions
var food = make(map[string]string)

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, text string) {
	s := strings.Split(text, ",")
	if len(s) == 1 {
		yelp_food_addr(bot, token, s[0], "台北市通化街")
	} else if len(s) >= 2 {
		yelp_food_addr(bot, token, s[0], s[1])
	}
}

func yelp_food_addr(bot *linebot.Client, token string, food string, addr string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	// make a simple query for food and location
	results, err := client.DoSimpleSearch(food, addr)
	if err != nil {
		log.Println(err)
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！\n請重新輸入\n\n吃吃 牛肉麵;台北市通化街")).Do()
	}

	for j := 0; j < 3; j++ {
		i := 0
		if results.Total >= 20 {
			i = rand.Intn(20)
		} else if results.Total >= 10 {
			i = rand.Intn(10)
		} else if results.Total > j {
			i = j
		} else if results.Total <= j && results.Total != 0 {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl)).Do()
		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<commit_msg>yelp rest ReplyMessage with multiple messages<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/JustinBeckwith/go-yelp/yelp"
//	"github.com/guregu/null"
	"github.com/line/line-bot-sdk-go/linebot"
)

var o *yelp.AuthOptions
var food = make(map[string]string)

type UrlShortener struct {
	ShortUrl    string
	OriginalUrl string
}

func yelp_init() {
	rand.Seed(time.Now().UnixNano())

	// check environment variables
	o = &yelp.AuthOptions{
		ConsumerKey:       os.Getenv("CONSUMER_KEY"),
		ConsumerSecret:    os.Getenv("CONSUMER_SECRET"),
		AccessToken:       os.Getenv("ACCESS_TOKEN"),
		AccessTokenSecret: os.Getenv("ACCESS_TOKEN_SECRET"),
	}

	if o.ConsumerKey == "" || o.ConsumerSecret == "" || o.AccessToken == "" || o.AccessTokenSecret == "" {
		log.Fatal("Wrong environment setting about yelp-api-keys")
	}
}

func yelp_parse(bot *linebot.Client, token string, text string) {
	s := strings.Split(text, ",")
	if len(s) == 1 {
		yelp_food_addr(bot, token, s[0], "台北市通化街")
	} else if len(s) >= 2 {
		yelp_food_addr(bot, token, s[0], s[1])
	}
}

func yelp_food_addr(bot *linebot.Client, token string, food string, addr string) {
	var err error

	// create a new yelp client with the auth keys
	client := yelp.New(o, nil)

	// make a simple query for food and location
	results, err := client.DoSimpleSearch(food, addr)
	if err != nil {
		log.Println(err)
		_, err = bot.ReplyMessage(token, linebot.NewTextMessage("查無資料！\n請重新輸入\n\n吃吃 牛肉麵;台北市通化街")).Do()
	}

	for j := 0; j < 3; j++ {
		i := 0
		if results.Total >= 20 {
			i = rand.Intn(20)
		} else if results.Total >= 10 {
			i = rand.Intn(10)
		} else if results.Total > j {
			i = j
		} else if results.Total <= j && results.Total != 0 {
			_, err = bot.ReplyMessage(token, linebot.NewTextMessage("已無更多資料！")).Do()
			break
		}
		urlOrig := UrlShortener{}
		urlOrig.short(results.Businesses[i].MobileURL)
		address := strings.Join(results.Businesses[i].Location.DisplayAddress, ",")
		var largeImageURL = strings.Replace(results.Businesses[i].ImageURL, "ms.jpg", "l.jpg", 1)

		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL), linebot.NewTextMessage("店名："+results.Businesses[i].Name+"\n電話："+results.Businesses[i].Phone+"\n評比："+strconv.FormatFloat(float64(results.Businesses[i].Rating), 'f', 1, 64)+"\n更多資訊："+urlOrig.ShortUrl), linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewImageMessage(largeImageURL, largeImageURL)).Do()
//		_, err = bot.ReplyMessage(token, linebot.NewLocationMessage(results.Businesses[i].Name+"\n", address, float64(results.Businesses[i].Location.Coordinate.Latitude), float64(results.Businesses[i].Location.Coordinate.Longitude))).Do()
	}
}

func getResponseData(urlOrig string) string {
	response, err := http.Get(urlOrig)
	if err != nil {
		log.Println(err)
	}
	defer response.Body.Close()
	contents, err := ioutil.ReadAll(response.Body)
	return string(contents)
}

func isGdShortener(urlOrig string) (string, string) {
	escapedUrl := url.QueryEscape(urlOrig)
	isGdUrl := fmt.Sprintf("http://is.gd/create.php?url=%s&format=simple", escapedUrl)
	return getResponseData(isGdUrl), urlOrig
}

func (u *UrlShortener) short(urlOrig string) *UrlShortener {
	shortUrl, originalUrl := isGdShortener(urlOrig)
	u.ShortUrl = shortUrl
	u.OriginalUrl = originalUrl
	return u
}
<|endoftext|>


<commit_before>// Package datatypes is a set of packages that provides many tools which helps you treating some complex structures and primitives types.
//
// The following packages are distributed under datatypes:
//  @datatypes/collection : Provides a new single-value list struct simplifying the iteration over it
//  @datatypes/dictionary : Provides a new object type which allows to treat them like <Key,Value> list
//  @datatypes/generic : Packages which includes some functionalities to treat 'generic' objects
//  @datatypes/string : Simple package which encapsulates some basic operations over strings
package datatypes

// Unused imports help docs.
import (
	// dictionary package
	_ "github.com/jaimelopez/datatypes/dictionary"
	// collection package
	_ "github.com/jaimelopez/datatypes/collection"
	// generic package
	_ "github.com/jaimelopez/datatypes/generic"
	// string package
	_ "github.com/jaimelopez/datatypes/string"
)
<commit_msg>Test for godoc<commit_after>// Package datatypes is a set of packages that provides many tools which helps you treating some complex structures and primitives types.
//
// The following packages are distributed under datatypes:
//  @datatypes/collection : Provides a new single-value list struct simplifying the iteration over it
//  @datatypes/dictionary : Provides a new object type which allows to treat them like <Key,Value> list
//  @datatypes/generic : Packages which includes some functionalities to treat 'generic' objects
//  @datatypes/string : Simple package which encapsulates some basic operations over strings
package datatypes

// Unused imports help docs.
import (
	// collection package
	_ "github.com/jaimelopez/datatypes/collection"

	// dictionary package
	_ "github.com/jaimelopez/datatypes/dictionary"

	// generic package
	_ "github.com/jaimelopez/datatypes/generic"

	// string package
	_ "github.com/jaimelopez/datatypes/string"
)
<|endoftext|>


<commit_before>// Copyright e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sys

import (
	"fmt"
	"io"

	"github.com/e-XpertSolutions/f5-rest-client/f5"
)

// FileIFileConfigList holds a list of FileIFile configuration.
type FileIFileConfigList struct {
	Items    []FileIFileConfig `json:"items"`
	Kind     string            `json:"kind"`
	SelfLink string            `json:"selflink"`
}

// FileIFileConfig holds the configuration of a single FileIFile.
type FileIFileConfig struct {
	Kind           string `json:"kind,omitempty"`
	Name           string `json:"name,omitempty"`
	Partition      string `json:"partition,omitempty"`
	FullPath       string `json:"fullPath,omitempty"`
	Generation     int    `json:"generation,omitempty"`
	SelfLink       string `json:"selfLink,omitempty"`
	Checksum       string `json:"checksum,omitempty"`
	CreateTime     string `json:"createTime,omitempty"`
	CreatedBy      string `json:"createdBy,omitempty"`
	LastUpdateTime string `json:"lastUpdateTime,omitempty"`
	Mode           int    `json:"mode,omitempty"`
	Revision       int    `json:"revision,omitempty"`
	Size           int    `json:"size,omitempty"`
	UpdatedBy      string `json:"updatedBy,omitempty"`
}

// FileIFileEndpoint represents the REST resource for managing FileIFile.
const FileIFileEndpoint = "/file/ifile"

// FileIFileResource provides an API to manage FileIFile configurations.
type FileIFileResource struct {
	c *f5.Client
}

// ListAll  lists all the FileIFile configurations.
func (r *FileIFileResource) ListAll() (*FileIFileConfigList, error) {
	var list FileIFileConfigList
	if err := r.c.ReadQuery(BasePath+FileIFileEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// Get a single FileIFile configuration identified by id.
func (r *FileIFileResource) Get(id string) (*FileIFileConfig, error) {
	var item FileIFileConfig
	if err := r.c.ReadQuery(BasePath+FileIFileEndpoint, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// CreateFromFile uploads an iFile and create a new entry in the
// iFiles list.
func (r *FileIFileResource) CreateFromFile(filename string, file io.Reader, filesize int64) error {
	uploadResp, err := r.c.UploadFile(file, filename, filesize)
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	data := map[string]string{
		"name":        filename,
		"source-path": "file:" + uploadResp.LocalFilePath,
	}
	if err := r.c.ModQuery("POST", BasePath+FileIFileEndpoint, data); err != nil {
		return fmt.Errorf("failed to import ifile: %v", err)
	}
	return nil
}

// EditFromFile uploads an iFile and update an existing entry in the
// iFiles list.
func (r *FileIFileResource) EditFromFile(filename string, file io.Reader, filesize int64) error {
	uploadResp, err := r.c.UploadFile(file, filename, filesize)
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	data := map[string]string{
		"name":        filename,
		"source-path": "file:" + uploadResp.LocalFilePath,
	}
	if err := r.c.ModQuery("PUT", BasePath+FileIFileEndpoint+"/"+filename, data); err != nil {
		return fmt.Errorf("failed to update ifile: %v", err)
	}
	return nil
}

// Delete a single FileIFile configuration identified by id.
func (r *FileIFileResource) Delete(id string) error {
	if err := r.c.ModQuery("DELETE", BasePath+FileIFileEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}
<commit_msg>f5/sys: add missing resource id<commit_after>// Copyright e-Xpert Solutions SA. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sys

import (
	"fmt"
	"io"

	"github.com/e-XpertSolutions/f5-rest-client/f5"
)

// FileIFileConfigList holds a list of FileIFile configuration.
type FileIFileConfigList struct {
	Items    []FileIFileConfig `json:"items"`
	Kind     string            `json:"kind"`
	SelfLink string            `json:"selflink"`
}

// FileIFileConfig holds the configuration of a single FileIFile.
type FileIFileConfig struct {
	Kind           string `json:"kind,omitempty"`
	Name           string `json:"name,omitempty"`
	Partition      string `json:"partition,omitempty"`
	FullPath       string `json:"fullPath,omitempty"`
	Generation     int    `json:"generation,omitempty"`
	SelfLink       string `json:"selfLink,omitempty"`
	Checksum       string `json:"checksum,omitempty"`
	CreateTime     string `json:"createTime,omitempty"`
	CreatedBy      string `json:"createdBy,omitempty"`
	LastUpdateTime string `json:"lastUpdateTime,omitempty"`
	Mode           int    `json:"mode,omitempty"`
	Revision       int    `json:"revision,omitempty"`
	Size           int    `json:"size,omitempty"`
	UpdatedBy      string `json:"updatedBy,omitempty"`
}

// FileIFileEndpoint represents the REST resource for managing FileIFile.
const FileIFileEndpoint = "/file/ifile"

// FileIFileResource provides an API to manage FileIFile configurations.
type FileIFileResource struct {
	c *f5.Client
}

// ListAll  lists all the FileIFile configurations.
func (r *FileIFileResource) ListAll() (*FileIFileConfigList, error) {
	var list FileIFileConfigList
	if err := r.c.ReadQuery(BasePath+FileIFileEndpoint, &list); err != nil {
		return nil, err
	}
	return &list, nil
}

// Get a single FileIFile configuration identified by id.
func (r *FileIFileResource) Get(id string) (*FileIFileConfig, error) {
	var item FileIFileConfig
	if err := r.c.ReadQuery(BasePath+FileIFileEndpoint+"/"+id, &item); err != nil {
		return nil, err
	}
	return &item, nil
}

// CreateFromFile uploads an iFile and create a new entry in the
// iFiles list.
func (r *FileIFileResource) CreateFromFile(filename string, file io.Reader, filesize int64) error {
	uploadResp, err := r.c.UploadFile(file, filename, filesize)
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	data := map[string]string{
		"name":        filename,
		"source-path": "file:" + uploadResp.LocalFilePath,
	}
	if err := r.c.ModQuery("POST", BasePath+FileIFileEndpoint, data); err != nil {
		return fmt.Errorf("failed to import ifile: %v", err)
	}
	return nil
}

// EditFromFile uploads an iFile and update an existing entry in the
// iFiles list.
func (r *FileIFileResource) EditFromFile(filename string, file io.Reader, filesize int64) error {
	uploadResp, err := r.c.UploadFile(file, filename, filesize)
	if err != nil {
		return fmt.Errorf("failed to create upload request: %v", err)
	}
	data := map[string]string{
		"name":        filename,
		"source-path": "file:" + uploadResp.LocalFilePath,
	}
	if err := r.c.ModQuery("PUT", BasePath+FileIFileEndpoint+"/"+filename, data); err != nil {
		return fmt.Errorf("failed to update ifile: %v", err)
	}
	return nil
}

// Delete a single FileIFile configuration identified by id.
func (r *FileIFileResource) Delete(id string) error {
	if err := r.c.ModQuery("DELETE", BasePath+FileIFileEndpoint+"/"+id, nil); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package fasta

import (
	"github.com/craiglowe/gonomics/dna"
	"testing"
)

var seqThreeA, _ = dna.StringToBases("ACGTacgTCATCATCATTACTACTAC")
var seqThreeB, _ = dna.StringToBases("acgtACGTACGT")
var seqThreeC, _ = dna.StringToBases("ACGTACGTACGTT")
var rcSeqThreeA, _ = dna.StringToBases("GTAGTAGTAATGATGATGAcgtACGT")
var rcSeqThreeB, _ = dna.StringToBases("ACGTACGTacgt")
var rcSeqThreeC, _ = dna.StringToBases("AACGTACGTACGT")
var allRevCompTests = []struct {
	input    []Fasta
	expected []Fasta
}{
	{[]Fasta{{"apple", seqThreeA}, {"banana", seqThreeB}, {"carrot", seqThreeC}}, []Fasta{{"apple_revComp", rcSeqThreeA}, {"banana_revComp", rcSeqThreeB}, {"carrot_revComp", rcSeqThreeC}}},
}

func TestReverseComplement(t *testing.T) {
	for _, test := range allRevCompTests {
		ReverseComplementAll(test.input)
		if !AllAreEqual(test.input, test.expected) {
			t.Errorf("Expected reverse complement to give %v, but got %v.", test.input, test.expected)
		}
	}
}
<commit_msg>updated test file after revComp no longer changes name<commit_after>package fasta

import (
	"github.com/craiglowe/gonomics/dna"
	"testing"
)

var seqThreeA, _ = dna.StringToBases("ACGTacgTCATCATCATTACTACTAC")
var seqThreeB, _ = dna.StringToBases("acgtACGTACGT")
var seqThreeC, _ = dna.StringToBases("ACGTACGTACGTT")
var rcSeqThreeA, _ = dna.StringToBases("GTAGTAGTAATGATGATGAcgtACGT")
var rcSeqThreeB, _ = dna.StringToBases("ACGTACGTacgt")
var rcSeqThreeC, _ = dna.StringToBases("AACGTACGTACGT")
var allRevCompTests = []struct {
	input    []Fasta
	expected []Fasta
}{
	{[]Fasta{{"apple", seqThreeA}, {"banana", seqThreeB}, {"carrot", seqThreeC}}, []Fasta{{"apple", rcSeqThreeA}, {"banana", rcSeqThreeB}, {"carrot", rcSeqThreeC}}},
}

func TestReverseComplement(t *testing.T) {
	for _, test := range allRevCompTests {
		ReverseComplementAll(test.input)
		if !AllAreEqual(test.input, test.expected) {
			t.Errorf("Expected reverse complement to give %v, but got %v.", test.input, test.expected)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"

	"github.com/codegangsta/cli"
)

const (
	DefaultHTTPDHost = "localhost:8080" // TODO extract
)

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

// TODO execute game on http daemon
func run() error {
	app := cli.NewApp()
	app.Name = "abalone_ctl"
	app.Usage = "abalone management util"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "httpd",
			Value: DefaultHTTPDHost,
			Usage: "abalone_httpd listening addr:port",
		},
	}
	app.Commands = []cli.Command{
		UsersCmd,
	}
	return app.Run(os.Args)
}
<commit_msg>feat(ctl) add helper funcs<commit_after>package main

import (
	"fmt"
	"log"
	"os"

	"github.com/codegangsta/cli"
)

const (
	DefaultHTTPDHost = "localhost:8080" // TODO extract
)

const (
	TimeSimpleFmt = "Mon Jan 2 15:04:05"
)

func main() {
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

// TODO execute game on http daemon
func run() error {
	app := cli.NewApp()
	app.Name = "abalone_ctl"
	app.Usage = "abalone management util"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "httpd",
			Value: DefaultHTTPDHost,
			Usage: "abalone_httpd listening addr:port",
		},
	}
	app.Commands = []cli.Command{
		UsersCmd,
	}
	return app.Run(os.Args)
}

func ErrArgRequired(arg string) error {
	return fmt.Errorf("%s is required", arg)
}

func APIURL(c *cli.Context, path string) string {
	return fmt.Sprintf("http://%s%s", c.GlobalString("httpd"), path)
}
<|endoftext|>


<commit_before>package tape

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"github.com/labstack/echo"
)

// Exce exce
var Exce = new(Exception)

// Exception throw exception
type Exception struct{}

// OK throw exception if !ok
func (e Exception) OK(ok bool, message interface{}) {
	if !ok {
		panic(message)
	}
}

// Err throw exception if err!=nil
func (e Exception) Err(err error, message interface{}) {
	if err != nil {
		if message == "" {
			switch {
			case strings.HasPrefix(err.Error(), "Error 1046"):
				message = "查询表名错误"
			case strings.HasPrefix(err.Error(), "Error 1049"):
				message = "查询库名错误"
			case strings.HasPrefix(err.Error(), "Error 1054"):
				message = "查询列名错误"
			case strings.HasPrefix(err.Error(), "Error 1062"):
				message = "写入数据重复"
			case strings.HasPrefix(err.Error(), "Error 1064"):
				message = "查询语句错误"
			case strings.HasPrefix(err.Error(), "sql:"):
				message = "查询数据失败"
			case strings.HasPrefix(err.Error(), "code=400,message=Unmarshal type error"):
				message = "数据类型错误"
			case strings.HasPrefix(err.Error(), "rpc error: code = Unavailable desc"):
				message = "连接服务错误"
			case strings.HasPrefix(err.Error(), "rpc error: code = Unknown desc"):
				message = "调用服务错误"
			case strings.HasPrefix(err.Error(), "rpc error: code = Unimplemented desc"):
				message = "解析服务错误"
			default:
				message = err.Error()
			}
		}
		fmt.Println(reflect.TypeOf(err), err.Error(), message)
		panic(message)
	}
}

// Catch throw exception if recover err
func (e Exception) Catch(ctx echo.Context) func() {
	return func() {
		if r := recover(); r != nil {
			ctx.JSON(http.StatusOK, Hash{
				STATUS:  FAIL,
				MESSAGE: r,
			})
		}
	}
}
<commit_msg>upgrade to v2<commit_after>package tape

import (
	"fmt"
	"net/http"
	"reflect"
	"strings"

	"github.com/labstack/echo"
)

// Exce exce
var Exce = new(Exception)

// Exception throw exception
type Exception struct{}

// OK throw exception if !ok
func (e *Exception) OK(ok bool, message interface{}) {
	if !ok {
		panic(message)
	}
}

// Err throw exception if err!=nil
func (e *Exception) Err(err error, message interface{}) {
	if err != nil {
		if message == "" {
			switch {
			case strings.HasPrefix(err.Error(), "Error 1046"):
				message = "查询表名错误"
			case strings.HasPrefix(err.Error(), "Error 1049"):
				message = "查询库名错误"
			case strings.HasPrefix(err.Error(), "Error 1054"):
				message = "查询列名错误"
			case strings.HasPrefix(err.Error(), "Error 1062"):
				message = "写入数据重复"
			case strings.HasPrefix(err.Error(), "Error 1064"):
				message = "查询语句错误"
			case strings.HasPrefix(err.Error(), "sql:"):
				message = "查询数据失败"
			case strings.HasPrefix(err.Error(), "code=400,message=Unmarshal type error"):
				message = "数据类型错误"
			case strings.HasPrefix(err.Error(), "rpc error: code = Unavailable desc"):
				message = "连接服务错误"
			case strings.HasPrefix(err.Error(), "rpc error: code = Unknown desc"):
				message = "调用服务错误"
			case strings.HasPrefix(err.Error(), "rpc error: code = Unimplemented desc"):
				message = "解析服务错误"
			default:
				message = err.Error()
			}
		}
		fmt.Println(reflect.TypeOf(err), err.Error(), message)
		panic(message)
	}
}

// Catch throw exception if recover err
func (e *Exception) Catch(ctx echo.Context) func() {
	return func() {
		if r := recover(); r != nil {
			ctx.JSON(http.StatusOK, Hash{
				STATUS:  FAIL,
				MESSAGE: r,
			})
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package systests

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/gregor"
	"github.com/keybase/client/go/libkb"
	gregor1 "github.com/keybase/client/go/protocol/gregor1"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/service"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	context "golang.org/x/net/context"
)

type electronMock struct {
	libkb.Contextified
	errCh   chan error
	stateCh chan keybase1.PushStateArg
	oobmCh  chan gregor1.OutOfBandMessage
}

func (e *electronMock) PushState(ctx context.Context, a keybase1.PushStateArg) (err error) {
	e.G().Log.Debug("electronMock::PushState: %#v\n", a)
	e.stateCh <- a
	return nil
}

func (e *electronMock) PushOutOfBandMessages(_ context.Context, msgs []gregor1.OutOfBandMessage) error {
	for _, m := range msgs {
		e.oobmCh <- m
	}
	return nil
}

func newElectronMock(g *libkb.GlobalContext) *electronMock {
	return &electronMock{
		Contextified: libkb.NewContextified(g),
		errCh:        make(chan error, 1),
		stateCh:      make(chan keybase1.PushStateArg, 10),
		oobmCh:       make(chan gregor1.OutOfBandMessage, 10),
	}
}

// filterPubsubdItems removes pubsubd generated IBMs from state
func filterPubsubdItems(items []gregor1.ItemAndMetadata) (res []gregor1.ItemAndMetadata) {
	for _, i := range items {
		categoryStr := i.Category().String()
		if !strings.HasPrefix(categoryStr, "user.") &&
			!strings.HasPrefix(categoryStr, "home.") {
			res = append(res, i)
		}
	}
	return
}

func TestGregorForwardToElectron(t *testing.T) {
	tc := setupTest(t, "gregor")
	defer tc.Cleanup()
	tc1 := cloneContext(tc)

	svc := service.NewService(tc.G, false)
	startCh := svc.GetStartChannel()
	stopCh := make(chan error)
	go func() {
		tc.G.Log.Debug("+ Service.Run")
		err := svc.Run()
		tc.G.Log.Debug("- Service.Run")
		if err != nil {
			t.Logf("Running the service produced an error: %v", err)
		}
		stopCh <- err
	}()

	userInfo := randomUser("grgr")
	sui := signupUI{
		info:         userInfo,
		Contextified: libkb.NewContextified(tc.G),
	}
	tc.G.SetUI(&sui)
	signup := client.NewCmdSignupRunner(tc.G)
	signup.SetTest()

	// Wait for the server to start up
	<-startCh

	if err := signup.Run(); err != nil {
		t.Fatal(err)
	}

	var err error
	check := func() {
		if err != nil {
			t.Fatal(err)
		}
	}
	cli, xp, err := client.GetRPCClientWithContext(tc1.G)
	srv := rpc.NewServer(xp, nil)
	em := newElectronMock(tc.G)
	err = srv.Register(keybase1.GregorUIProtocol(em))
	check()
	ncli := keybase1.DelegateUiCtlClient{Cli: cli}

	// Spin until gregor comes up; it should come up after signup
	var ok bool
	for i := 0; !ok && i < 200; i++ {
		if ok = svc.HasGregor(); !ok {
			time.Sleep(50 * time.Millisecond)
		} else {
			tc.G.Log.Debug("spinning, waiting for gregor to come up (attempt %d)", i)
		}
	}
	if !ok {
		t.Fatal("Gregor never came up after we signed up")
	}

	svc.SetGregorPushStateFilter(func(m gregor.Message) bool {
		cat := m.ToInBandMessage().ToStateUpdateMessage().Creation().Category()
		return cat.String() != "user.identity_change" && cat.String() != "user.key_change"
	})
	err = ncli.RegisterGregorFirehose(context.TODO())
	check()

	select {
	case a := <-em.stateCh:
		if a.Reason != keybase1.PushReason_RECONNECTED {
			t.Fatal(fmt.Sprintf("got wrong reason: %v", a.Reason))
		}
		if d := len(filterPubsubdItems(a.State.Items_)); d != 0 {
			t.Fatal(fmt.Sprintf("Wrong number of items in state -- should have 0, but got %d", d))
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got a reconnect message")
	}

	msgID, err := svc.GregorInject("foo", []byte("bar"))
	check()
	err = svc.GregorInjectOutOfBandMessage("baz", []byte("bip"))
	check()

	checkState := func(s gregor1.State) {
		items := filterPubsubdItems(s.Items_)
		if n := len(items); n != 1 {
			t.Errorf("Expected one item back; got %d", n)
			return
		}
		i := items[0]
		if !bytes.Equal(i.Md_.MsgID_.Bytes(), msgID.Bytes()) {
			t.Error("Wrong gregor message ID received")
		}
		if i.Item_.Category_.String() != "foo" {
			t.Error("Wrong gregor category")
		}
		if string(i.Item_.Body_.Bytes()) != "bar" {
			t.Error("Wrong gregor body")
		}
	}

	// We get two push states, one from the local send, and one from receiving broadcast
	for i := 0; i < 2; i++ {
		select {
		case pushArg := <-em.stateCh:
			checkState(pushArg.State)
			if pushArg.Reason != keybase1.PushReason_NEW_DATA {
				t.Errorf("wrong reason for push: %v", pushArg.Reason)
			}
		case <-time.After(3 * time.Second):
			t.Fatalf("never got an IBM")
		}
	}

	pollForTrue(t, tc.G, func(i int) bool {
		select {
		case oobm := <-em.oobmCh:
			if oobm.System_ != "baz" {
				return false
			}
			if s := string(oobm.Body_); s != "bip" {
				return false
			}
			return true
		case <-time.After(3 * time.Second * libkb.CITimeMultiplier(tc.G)):
			return false
		}
	})

	svc.SimulateGregorCrashForTesting()
	select {
	case pushArg := <-em.stateCh:
		checkState(pushArg.State)
		if pushArg.Reason != keybase1.PushReason_RECONNECTED {
			t.Errorf("wrong reason for push: %v", pushArg.Reason)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an IBM")
	}

	gcli := keybase1.GregorClient{Cli: cli}
	state, err := gcli.GetState(context.TODO())
	check()
	checkState(state)

	if err := client.CtlServiceStop(tc.G); err != nil {
		t.Fatal(err)
	}
	// If the server failed, it's also an error
	if err := <-stopCh; err != nil {
		t.Fatal(err)
	}

}
<commit_msg>fix broken CI due to new stellar notification<commit_after>// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package systests

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
	"time"

	"github.com/keybase/client/go/client"
	"github.com/keybase/client/go/gregor"
	"github.com/keybase/client/go/libkb"
	gregor1 "github.com/keybase/client/go/protocol/gregor1"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"github.com/keybase/client/go/service"
	"github.com/keybase/go-framed-msgpack-rpc/rpc"
	context "golang.org/x/net/context"
)

type electronMock struct {
	libkb.Contextified
	errCh   chan error
	stateCh chan keybase1.PushStateArg
	oobmCh  chan gregor1.OutOfBandMessage
}

func (e *electronMock) PushState(ctx context.Context, a keybase1.PushStateArg) (err error) {
	e.G().Log.Debug("electronMock::PushState: %#v\n", a)
	e.stateCh <- a
	return nil
}

func (e *electronMock) PushOutOfBandMessages(_ context.Context, msgs []gregor1.OutOfBandMessage) error {
	for _, m := range msgs {
		e.oobmCh <- m
	}
	return nil
}

func newElectronMock(g *libkb.GlobalContext) *electronMock {
	return &electronMock{
		Contextified: libkb.NewContextified(g),
		errCh:        make(chan error, 1),
		stateCh:      make(chan keybase1.PushStateArg, 10),
		oobmCh:       make(chan gregor1.OutOfBandMessage, 10),
	}
}

// filterPubsubdItems removes pubsubd generated IBMs from state
func filterPubsubdItems(items []gregor1.ItemAndMetadata) (res []gregor1.ItemAndMetadata) {
	for _, i := range items {
		categoryStr := i.Category().String()
		if !strings.HasPrefix(categoryStr, "user.") &&
			!strings.HasPrefix(categoryStr, "stellar.") &&
			!strings.HasPrefix(categoryStr, "home.") {
			res = append(res, i)
		}
	}
	return
}

func TestGregorForwardToElectron(t *testing.T) {
	tc := setupTest(t, "gregor")
	defer tc.Cleanup()
	tc1 := cloneContext(tc)

	svc := service.NewService(tc.G, false)
	startCh := svc.GetStartChannel()
	stopCh := make(chan error)
	go func() {
		tc.G.Log.Debug("+ Service.Run")
		err := svc.Run()
		tc.G.Log.Debug("- Service.Run")
		if err != nil {
			t.Logf("Running the service produced an error: %v", err)
		}
		stopCh <- err
	}()

	userInfo := randomUser("grgr")
	sui := signupUI{
		info:         userInfo,
		Contextified: libkb.NewContextified(tc.G),
	}
	tc.G.SetUI(&sui)
	signup := client.NewCmdSignupRunner(tc.G)
	signup.SetTest()

	// Wait for the server to start up
	<-startCh

	if err := signup.Run(); err != nil {
		t.Fatal(err)
	}

	var err error
	check := func() {
		if err != nil {
			t.Fatal(err)
		}
	}
	cli, xp, err := client.GetRPCClientWithContext(tc1.G)
	srv := rpc.NewServer(xp, nil)
	em := newElectronMock(tc.G)
	err = srv.Register(keybase1.GregorUIProtocol(em))
	check()
	ncli := keybase1.DelegateUiCtlClient{Cli: cli}

	// Spin until gregor comes up; it should come up after signup
	var ok bool
	for i := 0; !ok && i < 200; i++ {
		if ok = svc.HasGregor(); !ok {
			time.Sleep(50 * time.Millisecond)
		} else {
			tc.G.Log.Debug("spinning, waiting for gregor to come up (attempt %d)", i)
		}
	}
	if !ok {
		t.Fatal("Gregor never came up after we signed up")
	}

	svc.SetGregorPushStateFilter(func(m gregor.Message) bool {
		cat := m.ToInBandMessage().ToStateUpdateMessage().Creation().Category()
		return cat.String() != "user.identity_change" && cat.String() != "user.key_change"
	})
	err = ncli.RegisterGregorFirehose(context.TODO())
	check()

	select {
	case a := <-em.stateCh:
		if a.Reason != keybase1.PushReason_RECONNECTED {
			t.Fatal(fmt.Sprintf("got wrong reason: %v", a.Reason))
		}
		if d := len(filterPubsubdItems(a.State.Items_)); d != 0 {
			t.Fatal(fmt.Sprintf("Wrong number of items in state -- should have 0, but got %d", d))
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got a reconnect message")
	}

	msgID, err := svc.GregorInject("foo", []byte("bar"))
	check()
	err = svc.GregorInjectOutOfBandMessage("baz", []byte("bip"))
	check()

	checkState := func(s gregor1.State) {
		items := filterPubsubdItems(s.Items_)
		if n := len(items); n != 1 {
			t.Errorf("Expected one item back; got %d", n)
			return
		}
		i := items[0]
		if !bytes.Equal(i.Md_.MsgID_.Bytes(), msgID.Bytes()) {
			t.Error("Wrong gregor message ID received")
		}
		if i.Item_.Category_.String() != "foo" {
			t.Error("Wrong gregor category")
		}
		if string(i.Item_.Body_.Bytes()) != "bar" {
			t.Error("Wrong gregor body")
		}
	}

	// We get two push states, one from the local send, and one from receiving broadcast
	for i := 0; i < 2; i++ {
		select {
		case pushArg := <-em.stateCh:
			checkState(pushArg.State)
			if pushArg.Reason != keybase1.PushReason_NEW_DATA {
				t.Errorf("wrong reason for push: %v", pushArg.Reason)
			}
		case <-time.After(3 * time.Second):
			t.Fatalf("never got an IBM")
		}
	}

	pollForTrue(t, tc.G, func(i int) bool {
		select {
		case oobm := <-em.oobmCh:
			if oobm.System_ != "baz" {
				return false
			}
			if s := string(oobm.Body_); s != "bip" {
				return false
			}
			return true
		case <-time.After(3 * time.Second * libkb.CITimeMultiplier(tc.G)):
			return false
		}
	})

	svc.SimulateGregorCrashForTesting()
	select {
	case pushArg := <-em.stateCh:
		checkState(pushArg.State)
		if pushArg.Reason != keybase1.PushReason_RECONNECTED {
			t.Errorf("wrong reason for push: %v", pushArg.Reason)
		}
	case <-time.After(3 * time.Second):
		t.Fatalf("never got an IBM")
	}

	gcli := keybase1.GregorClient{Cli: cli}
	state, err := gcli.GetState(context.TODO())
	check()
	checkState(state)

	if err := client.CtlServiceStop(tc.G); err != nil {
		t.Fatal(err)
	}
	// If the server failed, it's also an error
	if err := <-stopCh; err != nil {
		t.Fatal(err)
	}

}
<|endoftext|>


<commit_before>package graphql_test

import (
	"context"
	"errors"
	"reflect"
	"sync"
	"testing"
	"time"

	"github.com/samsarahq/thunder/graphql"
	"github.com/samsarahq/thunder/graphql/schemabuilder"
	"github.com/samsarahq/thunder/internal"
	"github.com/samsarahq/thunder/reactive"
)

type User struct {
	Name     string
	Age      int
	resource *reactive.Resource
}

type Slow struct {
}

func TestPathError(t *testing.T) {
	schema := schemabuilder.NewSchema()

	type Inner struct{}

	query := schema.Query()
	query.FieldFunc("inner", func() Inner {
		return Inner{}
	})

	query.FieldFunc("safe", func() error {
		return graphql.NewSafeError("safe safe")
	})

	_ = schema.Mutation()

	type Expensive struct{}

	inner := schema.Object("inner", Inner{})
	inner.FieldFunc("expensive", func(ctx context.Context) Expensive {
		return Expensive{}
	})
	inner.FieldFunc("inners", func(ctx context.Context) []Inner {
		return []Inner{Inner{}}
	})

	nested := schema.Object("expensive", Expensive{})
	nested.FieldFunc("expensives", func(ctx context.Context) []Expensive {
		return []Expensive{Expensive{}}
	})

	nested.FieldFunc("err", func() error {
		return errors.New("no good, bad")
	})

	builtSchema := schema.MustBuild()

	q := graphql.MustParse(`
		{
			inner { inners { expensive { expensives { err } } } }
        }`, nil)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	e := graphql.Executor{}
	_, err := e.Execute(context.Background(), builtSchema.Query, nil, q)
	if err == nil || err.Error() != "inner.inners.0.expensive.expensives.0.err: no good, bad" {
		t.Errorf("bad error: %v", err)
	}

	q = graphql.MustParse(`
		{
			safe
		}`, nil)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	e = graphql.Executor{}
	_, err = e.Execute(context.Background(), builtSchema.Query, nil, q)
	if err == nil || err.Error() != "safe safe" {
		t.Errorf("bad error: %v", err)
	}
	if _, ok := err.(graphql.SanitizedError); !ok {
		t.Errorf("safe not safe")
	}

}

// TestEndToEndAwaitAndCache tests that slow fields get run in parallel and cached.
//
// The test verifies that the `slow` field on user, which sleeps for 100ms, gets
// run in parallel by verifying the total runtime over several users.
//
// The test verifies that a `count` sub-field of the `slow` field is cached by
// invalidating a single `slow` call, and tracking the number of calls to count.
func TestEndToEndAwaitAndCache(t *testing.T) {
	users := []*User{
		{Name: "Alice", Age: 5, resource: reactive.NewResource()},
		{Name: "Bob", Age: 6, resource: reactive.NewResource()},
		{Name: "Charlie", Age: 7, resource: reactive.NewResource()},
	}

	var mu sync.Mutex
	calls := 0

	schema := schemabuilder.NewSchema()

	query := schema.Query()
	query.FieldFunc("users", func(ctx context.Context) []*User {
		return users
	})

	_ = schema.Mutation()

	user := schema.Object("User", User{})
	user.FieldFunc("slow", func(ctx context.Context, u *User) *Slow {
		reactive.AddDependency(ctx, u.resource)
		time.Sleep(100 * time.Millisecond)
		return new(Slow)
	})

	slow := schema.Object("Slow", Slow{})
	slow.FieldFunc("count", func() bool {
		mu.Lock()
		calls++
		mu.Unlock()
		return true
	})

	builtSchema := schema.MustBuild()

	q := graphql.MustParse(`
		{
			users {
				name
				slow { count }
            }
        }`, nil)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	results := make(chan interface{})

	start := time.Now()
	rerunner := reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		e := graphql.Executor{}
		result, err := e.Execute(ctx, builtSchema.Query, nil, q)
		if err != nil {
			t.Error(err)
		}

		results <- internal.AsJSON(result)
		return nil, nil
	}, 0)
	defer rerunner.Stop()

	result := <-results
	duration := time.Since(start)
	if duration > 150*time.Millisecond {
		t.Errorf("did not execute in parallel; duration %v > 150ms", duration)
	}
	if !reflect.DeepEqual(result, internal.ParseJSON(`
		{"users": [
			{"name": "Alice", "slow": {"count": true}},
			{"name": "Bob", "slow": {"count": true}},
			{"name": "Charlie", "slow": {"count": true}}
        ]}`)) {
		t.Error("bad value")
	}
	if calls != 3 {
		t.Errorf("expected 3 calls to slow, got %d", calls)
	}

	start = time.Now()
	users[0].resource.Strobe()
	result = <-results
	duration = time.Since(start)
	if duration > 150*time.Millisecond {
		t.Errorf("did not execute in parallel; duration %v > 150ms", duration)
	}
	if !reflect.DeepEqual(result, internal.ParseJSON(`
		{"users": [
			{"name": "Alice", "slow": {"count": true}},
			{"name": "Bob", "slow": {"count": true}},
			{"name": "Charlie", "slow": {"count": true}}
        ]}`)) {
		t.Error("bad value")
	}
	if calls != 4 {
		t.Errorf("expected 4 total calls to slow, got %d", calls)
	}
}

func verifyArgumentOption(t *testing.T, query graphql.Type, queryString string, variables map[string]interface{}, expectedResult string) {
	q := graphql.MustParse(queryString, variables)

	if err := graphql.PrepareQuery(query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	e := graphql.Executor{}
	result, err := e.Execute(context.Background(), query, nil, q)
	if err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(internal.AsJSON(result), internal.ParseJSON(expectedResult)) {
		t.Error(internal.AsJSON(result))
	}
}

// TestArgumentOptionality tests that optional arguments can be omitted from
// query variables and that mandatory arguments must be included.
func TestArgumentOptionality(t *testing.T) {
	schema := schemabuilder.NewSchema()
	query := schema.Query()

	query.FieldFunc("optional", func(args struct{ X *int64 }) int64 {
		if args.X != nil {
			return *args.X
		}
		return -1
	})

	query.FieldFunc("mandatory", func(args struct{ X int64 }) int64 {
		return args.X
	})

	_ = schema.Mutation()
	builtSchema := schema.MustBuild()
	emptyVariables := map[string]interface{}{}
	filledVariables := map[string]interface{}{
		"testArg": float64(5),
	}

	// An optional argument that is passed in returns successfully.
	verifyArgumentOption(t, builtSchema.Query, `
		query getOptional($testArg: int64) {
			optional(x: $testArg)
		}`, filledVariables, `{"optional": 5}`)

	// An optional argument that is omitted returns successfully.
	verifyArgumentOption(t, builtSchema.Query, `
			query getOptional($testArg: int64) {
				optional(x: $testArg)
			}`, emptyVariables, `{"optional": -1}`)

	// A mandatory argument that is passed in returns successfully.
	verifyArgumentOption(t, builtSchema.Query, `
		query getMandatory($testArg: int64!) {
			mandatory(x: $testArg)
		}`, filledVariables, `{"mandatory": 5}`)

	// A mandatory argument that is omitted causes an error.
	q := graphql.MustParse(`
		query getMandatory($testArg: int64!) {
			mandatory(x: $testArg)
		}`, emptyVariables)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err.Error() != "error parsing args for \"mandatory\": x: not a number" {
		t.Error(err)
	}
}
<commit_msg>graphql: delete test case that is no longer valid<commit_after>package graphql_test

import (
	"context"
	"errors"
	"reflect"
	"sync"
	"testing"
	"time"

	"github.com/samsarahq/thunder/graphql"
	"github.com/samsarahq/thunder/graphql/schemabuilder"
	"github.com/samsarahq/thunder/internal"
	"github.com/samsarahq/thunder/reactive"
)

type User struct {
	Name     string
	Age      int
	resource *reactive.Resource
}

type Slow struct {
}

func TestPathError(t *testing.T) {
	schema := schemabuilder.NewSchema()

	type Inner struct{}

	query := schema.Query()
	query.FieldFunc("inner", func() Inner {
		return Inner{}
	})

	query.FieldFunc("safe", func() error {
		return graphql.NewSafeError("safe safe")
	})

	_ = schema.Mutation()

	type Expensive struct{}

	inner := schema.Object("inner", Inner{})
	inner.FieldFunc("expensive", func(ctx context.Context) Expensive {
		return Expensive{}
	})
	inner.FieldFunc("inners", func(ctx context.Context) []Inner {
		return []Inner{Inner{}}
	})

	nested := schema.Object("expensive", Expensive{})
	nested.FieldFunc("expensives", func(ctx context.Context) []Expensive {
		return []Expensive{Expensive{}}
	})

	nested.FieldFunc("err", func() error {
		return errors.New("no good, bad")
	})

	builtSchema := schema.MustBuild()

	q := graphql.MustParse(`
		{
			inner { inners { expensive { expensives { err } } } }
        }`, nil)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	e := graphql.Executor{}
	_, err := e.Execute(context.Background(), builtSchema.Query, nil, q)
	if err == nil || err.Error() != "inner.inners.0.expensive.expensives.0.err: no good, bad" {
		t.Errorf("bad error: %v", err)
	}

	q = graphql.MustParse(`
		{
			safe
		}`, nil)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	e = graphql.Executor{}
	_, err = e.Execute(context.Background(), builtSchema.Query, nil, q)
	if err == nil || err.Error() != "safe safe" {
		t.Errorf("bad error: %v", err)
	}
	if _, ok := err.(graphql.SanitizedError); !ok {
		t.Errorf("safe not safe")
	}

}

// TestEndToEndAwaitAndCache tests that slow fields get run in parallel and cached.
//
// The test verifies that the `slow` field on user, which sleeps for 100ms, gets
// run in parallel by verifying the total runtime over several users.
//
// The test verifies that a `count` sub-field of the `slow` field is cached by
// invalidating a single `slow` call, and tracking the number of calls to count.
func TestEndToEndAwaitAndCache(t *testing.T) {
	users := []*User{
		{Name: "Alice", Age: 5, resource: reactive.NewResource()},
		{Name: "Bob", Age: 6, resource: reactive.NewResource()},
		{Name: "Charlie", Age: 7, resource: reactive.NewResource()},
	}

	var mu sync.Mutex
	calls := 0

	schema := schemabuilder.NewSchema()

	query := schema.Query()
	query.FieldFunc("users", func(ctx context.Context) []*User {
		return users
	})

	_ = schema.Mutation()

	user := schema.Object("User", User{})
	user.FieldFunc("slow", func(ctx context.Context, u *User) *Slow {
		reactive.AddDependency(ctx, u.resource)
		time.Sleep(100 * time.Millisecond)
		return new(Slow)
	})

	slow := schema.Object("Slow", Slow{})
	slow.FieldFunc("count", func() bool {
		mu.Lock()
		calls++
		mu.Unlock()
		return true
	})

	builtSchema := schema.MustBuild()

	q := graphql.MustParse(`
		{
			users {
				name
				slow { count }
            }
        }`, nil)

	if err := graphql.PrepareQuery(builtSchema.Query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	results := make(chan interface{})

	start := time.Now()
	rerunner := reactive.NewRerunner(context.Background(), func(ctx context.Context) (interface{}, error) {
		e := graphql.Executor{}
		result, err := e.Execute(ctx, builtSchema.Query, nil, q)
		if err != nil {
			t.Error(err)
		}

		results <- internal.AsJSON(result)
		return nil, nil
	}, 0)
	defer rerunner.Stop()

	result := <-results
	duration := time.Since(start)
	if duration > 150*time.Millisecond {
		t.Errorf("did not execute in parallel; duration %v > 150ms", duration)
	}
	if !reflect.DeepEqual(result, internal.ParseJSON(`
		{"users": [
			{"name": "Alice", "slow": {"count": true}},
			{"name": "Bob", "slow": {"count": true}},
			{"name": "Charlie", "slow": {"count": true}}
        ]}`)) {
		t.Error("bad value")
	}
	if calls != 3 {
		t.Errorf("expected 3 calls to slow, got %d", calls)
	}

	start = time.Now()
	users[0].resource.Strobe()
	result = <-results
	duration = time.Since(start)
	if duration > 150*time.Millisecond {
		t.Errorf("did not execute in parallel; duration %v > 150ms", duration)
	}
	if !reflect.DeepEqual(result, internal.ParseJSON(`
		{"users": [
			{"name": "Alice", "slow": {"count": true}},
			{"name": "Bob", "slow": {"count": true}},
			{"name": "Charlie", "slow": {"count": true}}
        ]}`)) {
		t.Error("bad value")
	}
	if calls != 4 {
		t.Errorf("expected 4 total calls to slow, got %d", calls)
	}
}

func verifyArgumentOption(t *testing.T, query graphql.Type, queryString string, variables map[string]interface{}, expectedResult string) {
	q := graphql.MustParse(queryString, variables)

	if err := graphql.PrepareQuery(query, q.SelectionSet); err != nil {
		t.Error(err)
	}

	e := graphql.Executor{}
	result, err := e.Execute(context.Background(), query, nil, q)
	if err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(internal.AsJSON(result), internal.ParseJSON(expectedResult)) {
		t.Error(internal.AsJSON(result))
	}
}

// TestArgumentOptionality tests that optional arguments can be omitted from
// query variables and that mandatory arguments must be included.
func TestArgumentOptionality(t *testing.T) {
	schema := schemabuilder.NewSchema()
	query := schema.Query()

	query.FieldFunc("optional", func(args struct{ X *int64 }) int64 {
		if args.X != nil {
			return *args.X
		}
		return -1
	})

	query.FieldFunc("mandatory", func(args struct{ X int64 }) int64 {
		return args.X
	})

	_ = schema.Mutation()
	builtSchema := schema.MustBuild()
	emptyVariables := map[string]interface{}{}
	filledVariables := map[string]interface{}{
		"testArg": float64(5),
	}

	// An optional argument that is passed in returns successfully.
	verifyArgumentOption(t, builtSchema.Query, `
		query getOptional($testArg: int64) {
			optional(x: $testArg)
		}`, filledVariables, `{"optional": 5}`)

	// An optional argument that is omitted returns successfully.
	verifyArgumentOption(t, builtSchema.Query, `
			query getOptional($testArg: int64) {
				optional(x: $testArg)
			}`, emptyVariables, `{"optional": -1}`)

	// A mandatory argument that is passed in returns successfully.
	verifyArgumentOption(t, builtSchema.Query, `
		query getMandatory($testArg: int64!) {
			mandatory(x: $testArg)
		}`, filledVariables, `{"mandatory": 5}`)
}
<|endoftext|>