<commit_before>package aws

import (
	"errors"
	"fmt"
	"log"
	"strconv"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAlbListenerRule() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAlbListenerRuleCreate,
		Read:   resourceAwsAlbListenerRuleRead,
		Update: resourceAwsAlbListenerRuleUpdate,
		Delete: resourceAwsAlbListenerRuleDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"listener_arn": {
				Type:     schema.TypeString,
				Required: true,
			},
			"priority": {
				Type:         schema.TypeInt,
				Required:     true,
				ValidateFunc: validateAwsAlbListenerRulePriority,
			},
			"action": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"target_group_arn": {
							Type:     schema.TypeString,
							Required: true,
						},
						"type": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateAwsAlbListenerActionType,
						},
					},
				},
			},
			"condition": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"field": {
							Type:         schema.TypeString,
							Optional:     true,
							ValidateFunc: validateAwsListenerRuleField,
						},
						"values": {
							Type:     schema.TypeList,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Optional: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsAlbListenerRuleCreate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	params := &elbv2.CreateRuleInput{
		ListenerArn: aws.String(d.Get("listener_arn").(string)),
		Priority:    aws.Int64(int64(d.Get("priority").(int))),
	}

	actions := d.Get("action").([]interface{})
	params.Actions = make([]*elbv2.Action, len(actions))
	for i, action := range actions {
		actionMap := action.(map[string]interface{})
		params.Actions[i] = &elbv2.Action{
			TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
			Type:           aws.String(actionMap["type"].(string)),
		}
	}

	conditions := d.Get("condition").([]interface{})
	params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
	for i, condition := range conditions {
		conditionMap := condition.(map[string]interface{})
		values := conditionMap["values"].([]interface{})
		params.Conditions[i] = &elbv2.RuleCondition{
			Field:  aws.String(conditionMap["field"].(string)),
			Values: make([]*string, len(values)),
		}
		for j, value := range values {
			params.Conditions[i].Values[j] = aws.String(value.(string))
		}
	}

	resp, err := elbconn.CreateRule(params)
	if err != nil {
		return errwrap.Wrapf("Error creating ALB Listener Rule: {{err}}", err)
	}

	if len(resp.Rules) == 0 {
		return errors.New("Error creating ALB Listener Rule: no rules returned in response")
	}

	d.SetId(*resp.Rules[0].RuleArn)

	return resourceAwsAlbListenerRuleRead(d, meta)
}

func resourceAwsAlbListenerRuleRead(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	resp, err := elbconn.DescribeRules(&elbv2.DescribeRulesInput{
		RuleArns: []*string{aws.String(d.Id())},
	})
	if err != nil {
		if isRuleNotFound(err) {
			log.Printf("[WARN] DescribeRules - removing %s from state", d.Id())
			d.SetId("")
			return nil
		}
		return errwrap.Wrapf(fmt.Sprintf("Error retrieving Rules for listener %s: {{err}}", d.Id()), err)
	}

	if len(resp.Rules) != 1 {
		return fmt.Errorf("Error retrieving Rule %q", d.Id())
	}

	rule := resp.Rules[0]

	d.Set("arn", rule.RuleArn)
	if priority, err := strconv.Atoi(*rule.Priority); err != nil {
		return errwrap.Wrapf("Cannot convert rule priority %q to int: {{err}}", err)
	} else {
		d.Set("priority", priority)
	}

	actions := make([]interface{}, len(rule.Actions))
	for i, action := range rule.Actions {
		actionMap := make(map[string]interface{})
		actionMap["target_group_arn"] = *action.TargetGroupArn
		actionMap["type"] = *action.Type
		actions[i] = actionMap
	}
	d.Set("action", actions)

	conditions := make([]interface{}, len(rule.Conditions))
	for i, condition := range rule.Conditions {
		conditionMap := make(map[string]interface{})
		conditionMap["field"] = *condition.Field
		conditionValues := make([]string, len(condition.Values))
		for k, value := range condition.Values {
			conditionValues[k] = *value
		}
		conditionMap["values"] = conditionValues
		conditions[i] = conditionMap
	}
	d.Set("condition", conditions)

	return nil
}

func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	params := &elbv2.ModifyRuleInput{
		RuleArn: aws.String(d.Id()),
	}

	actions := d.Get("action").([]interface{})
	params.Actions = make([]*elbv2.Action, len(actions))
	for i, action := range actions {
		actionMap := action.(map[string]interface{})
		params.Actions[i] = &elbv2.Action{
			TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
			Type:           aws.String(actionMap["type"].(string)),
		}
	}

	conditions := d.Get("condition").([]interface{})
	params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
	for i, condition := range conditions {
		conditionMap := condition.(map[string]interface{})
		values := conditionMap["values"].([]interface{})
		params.Conditions[i] = &elbv2.RuleCondition{
			Field:  aws.String(conditionMap["field"].(string)),
			Values: make([]*string, len(values)),
		}
		for j, value := range values {
			params.Conditions[i].Values[j] = aws.String(value.(string))
		}
	}

	resp, err := elbconn.ModifyRule(params)
	if err != nil {
		return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
	}

	if len(resp.Rules) == 0 {
		return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
	}

	return resourceAwsAlbListenerRuleRead(d, meta)
}

func resourceAwsAlbListenerRuleDelete(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	_, err := elbconn.DeleteRule(&elbv2.DeleteRuleInput{
		RuleArn: aws.String(d.Id()),
	})
	if err != nil && !isRuleNotFound(err) {
		return errwrap.Wrapf("Error deleting ALB Listener Rule: {{err}}", err)
	}
	return nil
}

func validateAwsAlbListenerRulePriority(v interface{}, k string) (ws []string, errors []error) {
	value := v.(int)
	if value < 1 || value > 99999 {
		errors = append(errors, fmt.Errorf("%q must be in the range 1-99999", k))
	}
	return
}

func validateAwsListenerRuleField(v interface{}, k string) (ws []string, errors []error) {
	value := v.(string)
	if len(value) > 64 {
		errors = append(errors, fmt.Errorf("%q must be a maximum of 64 characters", k))
	}
	return
}

func isRuleNotFound(err error) bool {
	elberr, ok := err.(awserr.Error)
	return ok && elberr.Code() == "RuleNotFound"
}
<commit_msg>provider/aws: Import aws_alb_listener_rule fix<commit_after>package aws

import (
	"errors"
	"fmt"
	"log"
	"strconv"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/elbv2"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsAlbListenerRule() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsAlbListenerRuleCreate,
		Read:   resourceAwsAlbListenerRuleRead,
		Update: resourceAwsAlbListenerRuleUpdate,
		Delete: resourceAwsAlbListenerRuleDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"arn": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"listener_arn": {
				Type:     schema.TypeString,
				Required: true,
			},
			"priority": {
				Type:         schema.TypeInt,
				Required:     true,
				ValidateFunc: validateAwsAlbListenerRulePriority,
			},
			"action": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"target_group_arn": {
							Type:     schema.TypeString,
							Required: true,
						},
						"type": {
							Type:         schema.TypeString,
							Required:     true,
							ValidateFunc: validateAwsAlbListenerActionType,
						},
					},
				},
			},
			"condition": {
				Type:     schema.TypeList,
				Required: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"field": {
							Type:         schema.TypeString,
							Optional:     true,
							ValidateFunc: validateAwsListenerRuleField,
						},
						"values": {
							Type:     schema.TypeList,
							Elem:     &schema.Schema{Type: schema.TypeString},
							Optional: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsAlbListenerRuleCreate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	params := &elbv2.CreateRuleInput{
		ListenerArn: aws.String(d.Get("listener_arn").(string)),
		Priority:    aws.Int64(int64(d.Get("priority").(int))),
	}

	actions := d.Get("action").([]interface{})
	params.Actions = make([]*elbv2.Action, len(actions))
	for i, action := range actions {
		actionMap := action.(map[string]interface{})
		params.Actions[i] = &elbv2.Action{
			TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
			Type:           aws.String(actionMap["type"].(string)),
		}
	}

	conditions := d.Get("condition").([]interface{})
	params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
	for i, condition := range conditions {
		conditionMap := condition.(map[string]interface{})
		values := conditionMap["values"].([]interface{})
		params.Conditions[i] = &elbv2.RuleCondition{
			Field:  aws.String(conditionMap["field"].(string)),
			Values: make([]*string, len(values)),
		}
		for j, value := range values {
			params.Conditions[i].Values[j] = aws.String(value.(string))
		}
	}

	resp, err := elbconn.CreateRule(params)
	if err != nil {
		return errwrap.Wrapf("Error creating ALB Listener Rule: {{err}}", err)
	}

	if len(resp.Rules) == 0 {
		return errors.New("Error creating ALB Listener Rule: no rules returned in response")
	}

	d.SetId(*resp.Rules[0].RuleArn)

	return resourceAwsAlbListenerRuleRead(d, meta)
}

func resourceAwsAlbListenerRuleRead(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	resp, err := elbconn.DescribeRules(&elbv2.DescribeRulesInput{
		RuleArns: []*string{aws.String(d.Id())},
	})
	if err != nil {
		if isRuleNotFound(err) {
			log.Printf("[WARN] DescribeRules - removing %s from state", d.Id())
			d.SetId("")
			return nil
		}
		return errwrap.Wrapf(fmt.Sprintf("Error retrieving Rules for listener %s: {{err}}", d.Id()), err)
	}

	if len(resp.Rules) != 1 {
		return fmt.Errorf("Error retrieving Rule %q", d.Id())
	}

	rule := resp.Rules[0]

	d.Set("arn", rule.RuleArn)
	// Rules are evaluated in priority order, from the lowest value to the highest value. The default rule has the lowest priority.
	if *rule.Priority == "default" {
		d.Set("priority", 99999)
	} else {
		if priority, err := strconv.Atoi(*rule.Priority); err != nil {
			return errwrap.Wrapf("Cannot convert rule priority %q to int: {{err}}", err)
		} else {
			d.Set("priority", priority)
		}
	}

	actions := make([]interface{}, len(rule.Actions))
	for i, action := range rule.Actions {
		actionMap := make(map[string]interface{})
		actionMap["target_group_arn"] = *action.TargetGroupArn
		actionMap["type"] = *action.Type
		actions[i] = actionMap
	}
	d.Set("action", actions)

	conditions := make([]interface{}, len(rule.Conditions))
	for i, condition := range rule.Conditions {
		conditionMap := make(map[string]interface{})
		conditionMap["field"] = *condition.Field
		conditionValues := make([]string, len(condition.Values))
		for k, value := range condition.Values {
			conditionValues[k] = *value
		}
		conditionMap["values"] = conditionValues
		conditions[i] = conditionMap
	}
	d.Set("condition", conditions)

	return nil
}

func resourceAwsAlbListenerRuleUpdate(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	params := &elbv2.ModifyRuleInput{
		RuleArn: aws.String(d.Id()),
	}

	actions := d.Get("action").([]interface{})
	params.Actions = make([]*elbv2.Action, len(actions))
	for i, action := range actions {
		actionMap := action.(map[string]interface{})
		params.Actions[i] = &elbv2.Action{
			TargetGroupArn: aws.String(actionMap["target_group_arn"].(string)),
			Type:           aws.String(actionMap["type"].(string)),
		}
	}

	conditions := d.Get("condition").([]interface{})
	params.Conditions = make([]*elbv2.RuleCondition, len(conditions))
	for i, condition := range conditions {
		conditionMap := condition.(map[string]interface{})
		values := conditionMap["values"].([]interface{})
		params.Conditions[i] = &elbv2.RuleCondition{
			Field:  aws.String(conditionMap["field"].(string)),
			Values: make([]*string, len(values)),
		}
		for j, value := range values {
			params.Conditions[i].Values[j] = aws.String(value.(string))
		}
	}

	resp, err := elbconn.ModifyRule(params)
	if err != nil {
		return errwrap.Wrapf("Error modifying ALB Listener Rule: {{err}}", err)
	}

	if len(resp.Rules) == 0 {
		return errors.New("Error modifying creating ALB Listener Rule: no rules returned in response")
	}

	return resourceAwsAlbListenerRuleRead(d, meta)
}

func resourceAwsAlbListenerRuleDelete(d *schema.ResourceData, meta interface{}) error {
	elbconn := meta.(*AWSClient).elbv2conn

	_, err := elbconn.DeleteRule(&elbv2.DeleteRuleInput{
		RuleArn: aws.String(d.Id()),
	})
	if err != nil && !isRuleNotFound(err) {
		return errwrap.Wrapf("Error deleting ALB Listener Rule: {{err}}", err)
	}
	return nil
}

func validateAwsAlbListenerRulePriority(v interface{}, k string) (ws []string, errors []error) {
	value := v.(int)
	if value < 1 || value > 99999 {
		errors = append(errors, fmt.Errorf("%q must be in the range 1-99999", k))
	}
	return
}

func validateAwsListenerRuleField(v interface{}, k string) (ws []string, errors []error) {
	value := v.(string)
	if len(value) > 64 {
		errors = append(errors, fmt.Errorf("%q must be a maximum of 64 characters", k))
	}
	return
}

func isRuleNotFound(err error) bool {
	elberr, ok := err.(awserr.Error)
	return ok && elberr.Code() == "RuleNotFound"
}
<|endoftext|>


<commit_before>package ast

// Walk traverses an AST in depth-first order: It starts by calling fn(node);
// node must not be nil.  If f returns true, Walk invokes f recursively for
// each of the non-nil children of node, followed by a call of f(nil).
func Walk(node Node, fn func(Node) bool) {
	if !fn(node) {
		return
	}

	switch n := node.(type) {
	case *ObjectList:
		for _, item := range n.Items {
			Walk(item, fn)
		}
	case *ObjectKey:
		// nothing to do
	case *ObjectItem:
		for _, k := range n.Keys {
			Walk(k, fn)
		}
		Walk(n.Val, fn)
	case *LiteralType:
		// nothing to do
	case *ListType:
		for _, l := range n.List {
			Walk(l, fn)
		}
	case *ObjectType:
		for _, l := range n.List.Items {
			Walk(l, fn)
		}
	}

	fn(nil)
}
<commit_msg>ast: print unknown node type<commit_after>package ast

import "fmt"

// Walk traverses an AST in depth-first order: It starts by calling fn(node);
// node must not be nil.  If f returns true, Walk invokes f recursively for
// each of the non-nil children of node, followed by a call of f(nil).
func Walk(node Node, fn func(Node) bool) {
	if !fn(node) {
		return
	}

	switch n := node.(type) {
	case *ObjectList:
		for _, item := range n.Items {
			Walk(item, fn)
		}
	case *ObjectKey:
		// nothing to do
	case *ObjectItem:
		for _, k := range n.Keys {
			Walk(k, fn)
		}
		Walk(n.Val, fn)
	case *LiteralType:
		// nothing to do
	case *ListType:
		for _, l := range n.List {
			Walk(l, fn)
		}
	case *ObjectType:
		for _, l := range n.List.Items {
			Walk(l, fn)
		}
	default:
		fmt.Printf(" unknown type: %T\n", n)
	}

	fn(nil)
}
<|endoftext|>


<commit_before>/*
 组件
*/
package casper

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/gogap/errors"
	log "github.com/golang/glog"
)

var components map[string]*Component = make(map[string]*Component)

// 端点
type EndPoint struct {
	Url    string
	MQType string
	mq
}

// 组件
type Component struct {
	Name        string
	Description string
	in          EndPoint
	app         *App // 当这是一个入口组件...

	outs    map[string]*EndPoint
	handler ComponentHandler
}

type ComponentHandler func(*Payload) (err error)
type ComponentHandlers map[string]ComponentHandler

func BuildComFromConfig(fileName string) {
	var conf struct {
		Components []struct {
			Name        string `json:"name"`
			Description string `json:"description"`
			Type        string `json:"type"`
			In          string `json:"in"`
		} `json:"components"`
	}

	r, err := os.Open(fileName)
	if err != nil {
		panic(err)
	}
	defer r.Close()

	if err = json.NewDecoder(r).Decode(&conf); err != nil {
		panic(err)
	}

	for i := 0; i < len(conf.Components); i++ {
		_, err := NewComponent(conf.Components[i].Name, conf.Components[i].Description, conf.Components[i].Type, conf.Components[i].In)
		if err != nil {
			panic(err)
		}
	}
}

func NewComponent(name, description, mqtype, in string) (*Component, error) {
	sname, smqtype, sin := strings.TrimSpace(name), strings.TrimSpace(mqtype), strings.TrimSpace(in)
	if sname == "" {
		return nil, fmt.Errorf("Component's name empty ERROR")
	}
	if sin == "" {
		return nil, fmt.Errorf("Component's addr empty ERROR")
	}
	if smqtype == "" {
		return nil, fmt.Errorf("Component's mq typpe empty ERROR")
	}

	com := &Component{
		Name:        sname,
		Description: description,
		in:          EndPoint{Url: sin, MQType: smqtype, mq: nil},
		app:         nil,
		outs:        make(map[string]*EndPoint),
		handler:     nil}

	components[com.Name] = com

	return com, nil
}

func GetComponentByName(name string) *Component {
	if component, ok := components[name]; ok {
		return component
	}
	return nil
}

func SetHandlers(handlers ComponentHandlers) {
	for name, handler := range handlers {
		if component := GetComponentByName(name); component != nil {
			component.SetHandler(handler)
		} else {
			panic(fmt.Errorf("component of %s not exist", name))
		}
	}
}

func (p *Component) SetHandler(handler ComponentHandler) {
	if handler == nil {
		panic("handler could not be nil")
	}
	p.handler = handler
}

func (p *Component) GetOutPoint(url string) *EndPoint {
	surl := strings.TrimSpace(url)
	if surl == "" {
		return nil
	}

	if _, ok := p.outs[surl]; !ok {
		p.outs[surl] = &EndPoint{Url: surl, MQType: p.in.MQType, mq: nil}
	}

	return p.outs[surl]
}

func (p *Component) Run() (err error) {
	log.Infoln("Component Run...", p.Name)

	// 创建MQ
	p.in.mq, err = NewMq(p.in.MQType, p.in.Url)
	if err != nil {
		return
	}

	// MQ 准备
	err = p.in.mq.Ready()
	if err != nil {
		return
	}

	// 开始监听
	go p.recvMonitor()

	return nil
}

func (p *Component) recvMonitor() {
	for {
		msg, err := p.in.mq.RecvMessage()
		if err != nil {
			log.Errorln(p.Name, "Error receiving message:", err.Error())
			continue
		}
		log.Infoln(p.Name, "Recv:", string(msg))

		comsg := new(ComponentMessage)
		if err := comsg.FromJson(msg); err != nil {
			log.Errorln(p.Name, "Msg's format error:", err.Error(), string(msg))
			continue
		}

		go p.SendMsg(comsg)
	}
}

func (p *Component) SendMsg(comsg *ComponentMessage) {
	// 就是打日志用的
	msg, _ := comsg.Serialize()
	smsg := string(msg)

	// 更新调用链
	comsg.chain = append(comsg.chain, p.in.Url)

	// deal path
	next := comsg.TopGraph()
	if next == p.in.Url {
		// 正常流程
		next = comsg.PopGraph()
		if next == "" {
			log.Warningln("next is nil. send to entrance:", smsg)
			next = comsg.entrance
		}

		// call worker
		var err error
		if p.handler != nil {
			err = p.handler(comsg.Payload)
			if err != nil {
				// 业务处理错误, 发给入口
				log.Errorln("worker error, send to entrance:", smsg)
				if errors.IsErrCode(err) == false {
					comsg.Payload.Code = 500
				} else {
					comsg.Payload.Code = err.(errors.ErrCode).Code()
				}
				comsg.Payload.Message = err.(errors.ErrCode).Error()
				next = comsg.entrance
				comsg.Payload.Result = nil
			}
		}

		// 正常发到下一站
		log.Infoln("sendToNext:", smsg)
		if _, err = p.sendToNext(next, comsg); err != nil {
			log.Errorf(p.Name, "sendToNext error: ", smsg)
		}
	} else if next == "" {
		// 消息流出错了或是已经走到了入口
		if p.app != nil {
			log.Infoln(p.Name, "Msg to entrance:", smsg)
			if err := p.app.recvMsg(comsg); err != nil {
				log.Errorln(p.Name, "msg to entrance err:", err.Error())
			}
		} else {
			// send to entrance
			log.Errorln("msg's next null, send to entrance", smsg)
			_, err := p.sendToNext(comsg.entrance, comsg)
			if err != nil {
				log.Errorln(p.Name, "msg's next null, send to entrance ERR", smsg)
			}
		}
	} else if next != p.in.Url {
		// 发给正确的站点
		log.Warningln(p.Name, "msg's real next is:", next)
		_, err := p.sendToNext(next, comsg)
		if err != nil {
			log.Errorln(p.Name, "send to real next ERR: ", string(msg))
		}
	}
}

func (p *Component) sendToNext(url string, msg *ComponentMessage) (total int, err error) {
	if url == "" {
		return 0, fmt.Errorf("sendTo nil url")
	}

	if _, ok := p.outs[url]; ok == false {
		p.outs[url] = &EndPoint{Url: url, MQType: p.in.MQType, mq: nil}
		p.outs[url].mq, err = NewMq(p.in.MQType, url)
		if err != nil {
			return
		}
	}

	var msgb []byte
	msgb, err = msg.Serialize()
	if err != nil {
		return
	}

	log.Infoln(p.Name, "sendToNext:", url, string(msgb))
	total, err = p.outs[url].mq.SendToNext(msgb)

	return
}
<commit_msg>handler's result comeback<commit_after>/*
 组件
*/
package casper

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"

	"github.com/gogap/errors"
	log "github.com/golang/glog"
)

var components map[string]*Component = make(map[string]*Component)

// 端点
type EndPoint struct {
	Url    string
	MQType string
	mq
}

// 组件
type Component struct {
	Name        string
	Description string
	in          EndPoint
	app         *App // 当这是一个入口组件...

	outs    map[string]*EndPoint
	handler ComponentHandler
}

type ComponentHandler func(*Payload) (result interface{}, err error)
type ComponentHandlers map[string]ComponentHandler

func BuildComFromConfig(fileName string) {
	var conf struct {
		Components []struct {
			Name        string `json:"name"`
			Description string `json:"description"`
			Type        string `json:"type"`
			In          string `json:"in"`
		} `json:"components"`
	}

	r, err := os.Open(fileName)
	if err != nil {
		panic(err)
	}
	defer r.Close()

	if err = json.NewDecoder(r).Decode(&conf); err != nil {
		panic(err)
	}

	for i := 0; i < len(conf.Components); i++ {
		_, err := NewComponent(conf.Components[i].Name, conf.Components[i].Description, conf.Components[i].Type, conf.Components[i].In)
		if err != nil {
			panic(err)
		}
	}
}

func NewComponent(name, description, mqtype, in string) (*Component, error) {
	sname, smqtype, sin := strings.TrimSpace(name), strings.TrimSpace(mqtype), strings.TrimSpace(in)
	if sname == "" {
		return nil, fmt.Errorf("Component's name empty ERROR")
	}
	if sin == "" {
		return nil, fmt.Errorf("Component's addr empty ERROR")
	}
	if smqtype == "" {
		return nil, fmt.Errorf("Component's mq typpe empty ERROR")
	}

	com := &Component{
		Name:        sname,
		Description: description,
		in:          EndPoint{Url: sin, MQType: smqtype, mq: nil},
		app:         nil,
		outs:        make(map[string]*EndPoint),
		handler:     nil}

	components[com.Name] = com

	return com, nil
}

func GetComponentByName(name string) *Component {
	if component, ok := components[name]; ok {
		return component
	}
	return nil
}

func SetHandlers(handlers ComponentHandlers) {
	for name, handler := range handlers {
		if component := GetComponentByName(name); component != nil {
			component.SetHandler(handler)
		} else {
			panic(fmt.Errorf("component of %s not exist", name))
		}
	}
}

func (p *Component) SetHandler(handler ComponentHandler) {
	if handler == nil {
		panic("handler could not be nil")
	}
	p.handler = handler
}

func (p *Component) GetOutPoint(url string) *EndPoint {
	surl := strings.TrimSpace(url)
	if surl == "" {
		return nil
	}

	if _, ok := p.outs[surl]; !ok {
		p.outs[surl] = &EndPoint{Url: surl, MQType: p.in.MQType, mq: nil}
	}

	return p.outs[surl]
}

func (p *Component) Run() (err error) {
	log.Infoln("Component Run...", p.Name)

	// 创建MQ
	p.in.mq, err = NewMq(p.in.MQType, p.in.Url)
	if err != nil {
		return
	}

	// MQ 准备
	err = p.in.mq.Ready()
	if err != nil {
		return
	}

	// 开始监听
	go p.recvMonitor()

	return nil
}

func (p *Component) recvMonitor() {
	for {
		msg, err := p.in.mq.RecvMessage()
		if err != nil {
			log.Errorln(p.Name, "Error receiving message:", err.Error())
			continue
		}
		log.Infoln(p.Name, "Recv:", string(msg))

		comsg := new(ComponentMessage)
		if err := comsg.FromJson(msg); err != nil {
			log.Errorln(p.Name, "Msg's format error:", err.Error(), string(msg))
			continue
		}

		go p.SendMsg(comsg)
	}
}

func (p *Component) SendMsg(comsg *ComponentMessage) {
	// 就是打日志用的
	msg, _ := comsg.Serialize()
	smsg := string(msg)

	// 更新调用链
	comsg.chain = append(comsg.chain, p.in.Url)

	// deal path
	next := comsg.TopGraph()
	if next == p.in.Url {
		// 正常流程
		next = comsg.PopGraph()
		if next == "" {
			log.Warningln("next is nil. send to entrance:", smsg)
			next = comsg.entrance
		}

		// call worker
		var ret interface{}
		var err error
		if p.handler != nil {
			ret, err = p.handler(comsg.Payload)
			if err != nil {
				// 业务处理错误, 发给入口
				log.Errorln("worker error, send to entrance:", smsg)
				if errors.IsErrCode(err) == false {
					comsg.Payload.Code = 500
				} else {
					comsg.Payload.Code = err.(errors.ErrCode).Code()
				}
				comsg.Payload.Message = err.(errors.ErrCode).Error()
				next = comsg.entrance
				comsg.Payload.Result = nil
			} else {
				comsg.Payload.Result = ret
			}
		}

		// 正常发到下一站
		log.Infoln("sendToNext:", smsg)
		if _, err = p.sendToNext(next, comsg); err != nil {
			log.Errorf(p.Name, "sendToNext error: ", smsg)
		}
	} else if next == "" {
		// 消息流出错了或是已经走到了入口
		if p.app != nil {
			log.Infoln(p.Name, "Msg to entrance:", smsg)
			if err := p.app.recvMsg(comsg); err != nil {
				log.Errorln(p.Name, "msg to entrance err:", err.Error())
			}
		} else {
			// send to entrance
			log.Errorln("msg's next null, send to entrance", smsg)
			_, err := p.sendToNext(comsg.entrance, comsg)
			if err != nil {
				log.Errorln(p.Name, "msg's next null, send to entrance ERR", smsg)
			}
		}
	} else if next != p.in.Url {
		// 发给正确的站点
		log.Warningln(p.Name, "msg's real next is:", next)
		_, err := p.sendToNext(next, comsg)
		if err != nil {
			log.Errorln(p.Name, "send to real next ERR: ", string(msg))
		}
	}
}

func (p *Component) sendToNext(url string, msg *ComponentMessage) (total int, err error) {
	if url == "" {
		return 0, fmt.Errorf("sendTo nil url")
	}

	if _, ok := p.outs[url]; ok == false {
		p.outs[url] = &EndPoint{Url: url, MQType: p.in.MQType, mq: nil}
		p.outs[url].mq, err = NewMq(p.in.MQType, url)
		if err != nil {
			return
		}
	}

	var msgb []byte
	msgb, err = msg.Serialize()
	if err != nil {
		return
	}

	log.Infoln(p.Name, "sendToNext:", url, string(msgb))
	total, err = p.outs[url].mq.SendToNext(msgb)

	return
}
<|endoftext|>


<commit_before>// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// 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 main

import (
	"fmt"
	"os"

	"github.com/vmware/vmw-guestinfo/rpcvmx"
	"github.com/vmware/vmw-guestinfo/vmcheck"
)

func main() {
	isVM, err := vmcheck.IsVirtualWorld()
	if err != nil {
		fmt.Println("error: %s", err.Error())
		os.Exit(-1)
	}

	if !isVM {
		fmt.Println("not in a virtual world... :(")
	}

	config := rpcvmx.NewConfig()

	fmt.Println(config.SetString("foo", "bar"))
	fmt.Println(config.String("foo", "foo"))

	fmt.Println(config.SetInt("foo", 3))
	fmt.Println(config.Int("foo", 0))

	fmt.Println(config.SetBool("foo", false))
	fmt.Println(config.Bool("foo", true))

}
<commit_msg>Avoid possible segmentation violation in example (#10)<commit_after>// Copyright 2016 VMware, Inc. All Rights Reserved.
//
// 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 main

import (
	"fmt"
	"log"

	"github.com/vmware/vmw-guestinfo/rpcvmx"
	"github.com/vmware/vmw-guestinfo/vmcheck"
)

func main() {
	isVM, err := vmcheck.IsVirtualWorld()
	if err != nil {
		log.Fatalf("error: %s", err.Error())
	}

	if !isVM {
		log.Fatal("not in a virtual world... :(")
	}

	config := rpcvmx.NewConfig()

	fmt.Println(config.SetString("foo", "bar"))
	fmt.Println(config.String("foo", "foo"))

	fmt.Println(config.SetInt("foo", 3))
	fmt.Println(config.Int("foo", 0))

	fmt.Println(config.SetBool("foo", false))
	fmt.Println(config.Bool("foo", true))

}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/jcla1/goquizduell"
	"github.com/jcla1/goquizduell/util"
	"os"
)

var numRandGames = flag.Int("randGames", 0, "number of random games to start")

func main() {
	flag.Parse()
	c := util.PrepareClient(os.Getenv("QD_USERNAME"), os.Getenv("QD_PASSWORD"), os.Getenv("QD_COOKIE_FILE"))

	games := c.GetUserGames().User.Games

	activeGameCount := 0

	for _, game := range games {
		if game.GameState == quizduell.Active {
			activeGameCount += 1
		}

		// First we accept any game requests
		if game.GameState == quizduell.Waiting && game.YourTurn {
			fmt.Println("Accepting invite from:", game.Opponent.Name)
			c.AcceptGame(game.ID)
		}

		// Answer the questions
		if game.YourTurn {
			numAns := findNumRequiredAns(game)
			categoryID := findCorrectCategoryID(game, numAns)
			answers := make([]int, numAns)

			fmt.Println("Answering", numAns, "questions against:", game.Opponent.Name)
			c.UploadRoundAnswers(game.ID, append(game.YourAnswers, answers...), categoryID)
		}
	}

	stats := c.CategoryStatistics()
	fmt.Println("---\nCurrently playing in", activeGameCount, "active games.")
	fmt.Printf("My current rank is %d/%d users.\n", stats.Rank, stats.NumUsers)
	// fmt.Println("So far you I have won", stats.GamesWon, "games with", stats.QuestionsCorrect, "correct answers.")
	fmt.Println("---")

	if *numRandGames == 0 && activeGameCount < 10 {
		*numRandGames = 10 - activeGameCount
	}

	for i := 0; i < *numRandGames; i++ {
		g := c.StartRandomGame()
		fmt.Println("Starting random game against:", g.Opponent.Name)
	}
}

func findCorrectCategoryID(game quizduell.Game, numAns int) int {
	if numAns == 3 && len(game.OpponentAnswers) != 0 {
		return game.CategoryChoices[len(game.CategoryChoices)-1]
	}
	// We don't care what category we choose otherwise!
	return 0
}

func findNumRequiredAns(game quizduell.Game) int {
	if len(game.OpponentAnswers) == 0 || len(game.OpponentAnswers) == 18 {
		return 3
	}
	return 6
}<commit_msg>Example player gives up on games that take too long, and doesn't play perfect games anymore, rather a GOOD random guess<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/jcla1/goquizduell"
	"github.com/jcla1/goquizduell/util"
	"math"
	"math/rand"
	"os"
)

var numRandGames = flag.Int("randGames", 0, "number of random games to start")

func main() {
	flag.Parse()
	c := util.PrepareClient(os.Getenv("QD_USERNAME"), os.Getenv("QD_PASSWORD"), os.Getenv("QD_COOKIE_FILE"))

	games := c.GetUserGames().User.Games

	activeGameCount := 0

	for _, game := range games {
		if game.GameState == quizduell.Active {
			activeGameCount += 1
		}

		// First we accept any game requests
		if game.GameState == quizduell.Waiting && game.YourTurn {
			fmt.Println("Accepting invite from:", game.Opponent.Name)
			c.AcceptGame(game.ID)
		}

		// Answer the questions
		if game.YourTurn {
			numAns := findNumRequiredAns(game)
			categoryID := findCorrectCategoryID(game, numAns)
			answers := make([]int, numAns)

			correctCount := 0
			for i := range answers {
				answers[i] = randAnswer()
				if answers[i] == 0 {
					correctCount++
				} else if answers[i] != 1 && answers[i] != 2 && answers[i] != 3 {
					panic(answers[i])
				}
			}

			fmt.Println("Answering", numAns, "questions against:", game.Opponent.Name, "[ correct:", correctCount, "]")
			c.UploadRoundAnswers(game.ID, append(game.YourAnswers, answers...), categoryID)
		}

		if !game.YourTurn && game.ElapsedMinutes > 60 {
			fmt.Println("Giving up game against:", game.Opponent.Name)
			c.GiveUp(game.ID)
		}
	}

	stats := c.CategoryStatistics()
	fmt.Println("---\nCurrently playing in", activeGameCount, "active games.")
	fmt.Printf("My current rank is %d/%d users.\n", stats.Rank, stats.NumUsers)
	// fmt.Println("So far you I have won", stats.GamesWon, "games with", stats.QuestionsCorrect, "correct answers.")
	fmt.Println("---")

	if *numRandGames == 0 && activeGameCount < 10 {
		*numRandGames = 10 - activeGameCount
	}

	for i := 0; i < *numRandGames; i++ {
		g := c.StartRandomGame()
		fmt.Println("Starting random game against:", g.Opponent.Name)
	}
}

func randAnswer() int {
	return int(math.Abs((rand.NormFloat64())))
}

func findCorrectCategoryID(game quizduell.Game, numAns int) int {
	if numAns == 3 && len(game.OpponentAnswers) != 0 {
		return game.CategoryChoices[len(game.CategoryChoices)-1]
	}
	// We don't care what category we choose otherwise!
	return 0
}

func findNumRequiredAns(game quizduell.Game) int {
	if len(game.OpponentAnswers) == 0 || len(game.OpponentAnswers) == 18 {
		return 3
	}
	return 6
}
<|endoftext|>


<commit_before>package runner

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	"github.com/cloudfoundry-incubator/garden/client"
	"github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/eapache/go-resiliency/retrier"
	"github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/lager/lagertest"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

const MNT_DETACH = 0x2

var RootFSPath = os.Getenv("GARDEN_TEST_ROOTFS")
var DataDir = os.Getenv("GARDEN_TEST_GRAPHPATH")
var TarBin = os.Getenv("GARDEN_TAR_PATH")

type RunningGarden struct {
	client.Client

	runner  *ginkgomon.Runner
	process ifrit.Process

	debugIP   string
	debugPort int

	Pid int

	Tmpdir string

	DepotDir  string
	DataDir   string
	GraphPath string

	logger lager.Logger
}

func Start(bin, initBin, iodaemonBin, nstarBin, dadooBin string, supplyDefaultRootfs bool, argv ...string) *RunningGarden {
	network := "unix"
	addr := fmt.Sprintf("/tmp/garden_%d.sock", GinkgoParallelNode())
	tmpDir := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()),
	)

	if DataDir == "" {
		// This must be set outside of the Ginkgo node directory (tmpDir) because
		// otherwise the Concourse worker may run into one of the AUFS kernel
		// module bugs that cause the VM to become unresponsive.
		DataDir = "/tmp/aufs_mount"
	}

	graphPath := filepath.Join(DataDir, fmt.Sprintf("node-%d", ginkgo.GinkgoParallelNode()))
	depotDir := filepath.Join(tmpDir, "containers")

	MustMountTmpfs(graphPath)

	r := &RunningGarden{
		DepotDir: depotDir,

		DataDir:   DataDir,
		GraphPath: graphPath,
		Tmpdir:    tmpDir,
		logger:    lagertest.NewTestLogger("garden-runner"),

		Client: client.New(connection.New(network, addr)),
	}

	c := cmd(tmpDir, depotDir, graphPath, network, addr, bin, initBin, iodaemonBin, nstarBin, dadooBin, TarBin, supplyDefaultRootfs, argv...)
	c.Env = append(os.Environ(), fmt.Sprintf("TMPDIR=%s", tmpDir))
	r.runner = ginkgomon.New(ginkgomon.Config{
		Name:              "guardian",
		Command:           c,
		AnsiColorCode:     "31m",
		StartCheck:        "guardian.started",
		StartCheckTimeout: 30 * time.Second,
	})
	r.process = ifrit.Invoke(r.runner)
	r.Pid = c.Process.Pid

	for i, arg := range c.Args {
		if arg == "--debug-bind-ip" {
			r.debugIP = c.Args[i+1]
		}
		if arg == "--debug-bind-port" {
			r.debugPort, _ = strconv.Atoi(c.Args[i+1])
		}
	}

	return r
}

func (r *RunningGarden) Kill() error {
	r.process.Signal(syscall.SIGKILL)
	select {
	case err := <-r.process.Wait():
		return err
	case <-time.After(time.Second * 10):
		r.process.Signal(syscall.SIGKILL)
		return errors.New("timed out waiting for garden to shutdown after 10 seconds")
	}
}

func (r *RunningGarden) DestroyAndStop() error {
	if err := r.DestroyContainers(); err != nil {
		return err
	}

	if err := r.Stop(); err != nil {
		return err
	}

	return nil
}

func (r *RunningGarden) Stop() error {
	r.process.Signal(syscall.SIGTERM)

	var err error
	for i := 0; i < 5; i++ {
		select {
		case err := <-r.process.Wait():
			return err
		case <-time.After(time.Second * 5):
			r.process.Signal(syscall.SIGTERM)
			err = errors.New("timed out waiting for garden to shutdown after 5 seconds")
		}
	}

	r.process.Signal(syscall.SIGKILL)
	return err
}

func cmd(tmpdir, depotDir, graphPath, network, addr, bin, initBin, iodaemonBin, nstarBin, dadooBin, tarBin string, supplyDefaultRootfs bool, argv ...string) *exec.Cmd {
	Expect(os.MkdirAll(tmpdir, 0755)).To(Succeed())

	snapshotsPath := filepath.Join(tmpdir, "snapshots")

	Expect(os.MkdirAll(depotDir, 0755)).To(Succeed())

	Expect(os.MkdirAll(snapshotsPath, 0755)).To(Succeed())

	appendDefaultFlag := func(ar []string, key, value string) []string {
		for _, a := range argv {
			if a == key {
				return ar
			}
		}

		if value != "" {
			return append(ar, key, value)
		} else {
			return append(ar, key)
		}
	}

	gardenArgs := make([]string, len(argv))
	copy(gardenArgs, argv)

	switch network {
	case "tcp":
		gardenArgs = appendDefaultFlag(gardenArgs, "--bind-ip", addr)
	case "unix":
		gardenArgs = appendDefaultFlag(gardenArgs, "--bind-socket", addr)
	}

	if supplyDefaultRootfs {
		gardenArgs = appendDefaultFlag(gardenArgs, "--default-rootfs", RootFSPath)
	}

	gardenArgs = appendDefaultFlag(gardenArgs, "--depot", depotDir)
	gardenArgs = appendDefaultFlag(gardenArgs, "--graph", graphPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--tag", fmt.Sprintf("%d", GinkgoParallelNode()))
	gardenArgs = appendDefaultFlag(gardenArgs, "--network-pool", fmt.Sprintf("10.254.%d.0/22", 4*GinkgoParallelNode()))
	gardenArgs = appendDefaultFlag(gardenArgs, "--init-bin", initBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--iodaemon-bin", iodaemonBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--dadoo-bin", dadooBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--nstar-bin", nstarBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--tar-bin", tarBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--debug-bind-ip", "0.0.0.0")
	gardenArgs = appendDefaultFlag(gardenArgs, "--debug-bind-port", fmt.Sprintf("%d", 8080+ginkgo.GinkgoParallelNode()))

	return exec.Command(bin, gardenArgs...)
}

func (r *RunningGarden) Cleanup() {
	// unmount aufs since the docker graph driver leaves this around,
	// otherwise the following commands might fail
	retry := retrier.New(retrier.ConstantBackoff(200, 500*time.Millisecond), nil)

	err := retry.Run(func() error {
		if err := os.RemoveAll(path.Join(r.GraphPath, "aufs")); err == nil {
			return nil // if we can remove it, it's already unmounted
		}

		if err := syscall.Unmount(path.Join(r.GraphPath, "aufs"), MNT_DETACH); err != nil {
			r.logger.Error("failed-unmount-attempt", err)
			return err
		}

		return nil
	})

	if err != nil {
		r.logger.Error("failed-to-unmount", err)
	}

	MustUnmountTmpfs(r.GraphPath)

	// In the kernel version 3.19.0-51-generic the code bellow results in
	// hanging the running VM. We are not deleting the node-X directories. They
	// are empty and the next test will re-use them. We will stick with that
	// workaround until we can test on a newer kernel that will hopefully not
	// have this bug.
	//
	// if err := os.RemoveAll(r.GraphPath); err != nil {
	// 	r.logger.Error("remove-graph", err)
	// }

	r.logger.Info("cleanup-tempdirs")
	if err := os.RemoveAll(r.Tmpdir); err != nil {
		r.logger.Error("cleanup-tempdirs-failed", err, lager.Data{"tmpdir": r.Tmpdir})
	} else {
		r.logger.Info("tempdirs-removed")
	}
}

func (r *RunningGarden) DestroyContainers() error {
	containers, err := r.Containers(nil)
	if err != nil {
		return err
	}

	for _, container := range containers {
		r.Destroy(container.Handle())
	}

	return nil
}

type debugVars struct {
	NumGoRoutines int `json:"numGoRoutines"`
}

func (r *RunningGarden) NumGoroutines() (int, error) {
	debugURL := fmt.Sprintf("http://%s:%d/debug/vars", r.debugIP, r.debugPort)
	res, err := http.Get(debugURL)
	defer res.Body.Close()
	if err != nil {
		return 0, err
	}

	decoder := json.NewDecoder(res.Body)
	var debugVarsData debugVars
	err = decoder.Decode(&debugVarsData)
	if err != nil {
		return 0, err
	}

	return debugVarsData.NumGoRoutines, nil
}

func (r *RunningGarden) Buffer() *gbytes.Buffer {
	return r.runner.Buffer()
}

func (r *RunningGarden) ExitCode() int {
	return r.runner.ExitCode()
}
<commit_msg>Isolate port pools in different workers<commit_after>package runner

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	"github.com/cloudfoundry-incubator/garden/client"
	"github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/eapache/go-resiliency/retrier"
	"github.com/onsi/ginkgo"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/lager/lagertest"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

const MNT_DETACH = 0x2

var RootFSPath = os.Getenv("GARDEN_TEST_ROOTFS")
var DataDir = os.Getenv("GARDEN_TEST_GRAPHPATH")
var TarBin = os.Getenv("GARDEN_TAR_PATH")

type RunningGarden struct {
	client.Client

	runner  *ginkgomon.Runner
	process ifrit.Process

	debugIP   string
	debugPort int

	Pid int

	Tmpdir string

	DepotDir  string
	DataDir   string
	GraphPath string

	logger lager.Logger
}

func Start(bin, initBin, iodaemonBin, nstarBin, dadooBin string, supplyDefaultRootfs bool, argv ...string) *RunningGarden {
	network := "unix"
	addr := fmt.Sprintf("/tmp/garden_%d.sock", GinkgoParallelNode())
	tmpDir := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()),
	)

	if DataDir == "" {
		// This must be set outside of the Ginkgo node directory (tmpDir) because
		// otherwise the Concourse worker may run into one of the AUFS kernel
		// module bugs that cause the VM to become unresponsive.
		DataDir = "/tmp/aufs_mount"
	}

	graphPath := filepath.Join(DataDir, fmt.Sprintf("node-%d", ginkgo.GinkgoParallelNode()))
	depotDir := filepath.Join(tmpDir, "containers")

	MustMountTmpfs(graphPath)

	r := &RunningGarden{
		DepotDir: depotDir,

		DataDir:   DataDir,
		GraphPath: graphPath,
		Tmpdir:    tmpDir,
		logger:    lagertest.NewTestLogger("garden-runner"),

		Client: client.New(connection.New(network, addr)),
	}

	c := cmd(tmpDir, depotDir, graphPath, network, addr, bin, initBin, iodaemonBin, nstarBin, dadooBin, TarBin, supplyDefaultRootfs, argv...)
	c.Env = append(os.Environ(), fmt.Sprintf("TMPDIR=%s", tmpDir))
	r.runner = ginkgomon.New(ginkgomon.Config{
		Name:              "guardian",
		Command:           c,
		AnsiColorCode:     "31m",
		StartCheck:        "guardian.started",
		StartCheckTimeout: 30 * time.Second,
	})
	r.process = ifrit.Invoke(r.runner)
	r.Pid = c.Process.Pid

	for i, arg := range c.Args {
		if arg == "--debug-bind-ip" {
			r.debugIP = c.Args[i+1]
		}
		if arg == "--debug-bind-port" {
			r.debugPort, _ = strconv.Atoi(c.Args[i+1])
		}
	}

	return r
}

func (r *RunningGarden) Kill() error {
	r.process.Signal(syscall.SIGKILL)
	select {
	case err := <-r.process.Wait():
		return err
	case <-time.After(time.Second * 10):
		r.process.Signal(syscall.SIGKILL)
		return errors.New("timed out waiting for garden to shutdown after 10 seconds")
	}
}

func (r *RunningGarden) DestroyAndStop() error {
	if err := r.DestroyContainers(); err != nil {
		return err
	}

	if err := r.Stop(); err != nil {
		return err
	}

	return nil
}

func (r *RunningGarden) Stop() error {
	r.process.Signal(syscall.SIGTERM)

	var err error
	for i := 0; i < 5; i++ {
		select {
		case err := <-r.process.Wait():
			return err
		case <-time.After(time.Second * 5):
			r.process.Signal(syscall.SIGTERM)
			err = errors.New("timed out waiting for garden to shutdown after 5 seconds")
		}
	}

	r.process.Signal(syscall.SIGKILL)
	return err
}

func cmd(tmpdir, depotDir, graphPath, network, addr, bin, initBin, iodaemonBin, nstarBin, dadooBin, tarBin string, supplyDefaultRootfs bool, argv ...string) *exec.Cmd {
	Expect(os.MkdirAll(tmpdir, 0755)).To(Succeed())

	snapshotsPath := filepath.Join(tmpdir, "snapshots")

	Expect(os.MkdirAll(depotDir, 0755)).To(Succeed())

	Expect(os.MkdirAll(snapshotsPath, 0755)).To(Succeed())

	appendDefaultFlag := func(ar []string, key, value string) []string {
		for _, a := range argv {
			if a == key {
				return ar
			}
		}

		if value != "" {
			return append(ar, key, value)
		} else {
			return append(ar, key)
		}
	}

	gardenArgs := make([]string, len(argv))
	copy(gardenArgs, argv)

	switch network {
	case "tcp":
		gardenArgs = appendDefaultFlag(gardenArgs, "--bind-ip", addr)
	case "unix":
		gardenArgs = appendDefaultFlag(gardenArgs, "--bind-socket", addr)
	}

	if supplyDefaultRootfs {
		gardenArgs = appendDefaultFlag(gardenArgs, "--default-rootfs", RootFSPath)
	}

	gardenArgs = appendDefaultFlag(gardenArgs, "--depot", depotDir)
	gardenArgs = appendDefaultFlag(gardenArgs, "--graph", graphPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--tag", fmt.Sprintf("%d", GinkgoParallelNode()))
	gardenArgs = appendDefaultFlag(gardenArgs, "--network-pool", fmt.Sprintf("10.254.%d.0/22", 4*GinkgoParallelNode()))
	gardenArgs = appendDefaultFlag(gardenArgs, "--init-bin", initBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--iodaemon-bin", iodaemonBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--dadoo-bin", dadooBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--nstar-bin", nstarBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--tar-bin", tarBin)
	gardenArgs = appendDefaultFlag(gardenArgs, "--port-pool-start", fmt.Sprintf("%d", GinkgoParallelNode()*10000))
	gardenArgs = appendDefaultFlag(gardenArgs, "--debug-bind-ip", "0.0.0.0")
	gardenArgs = appendDefaultFlag(gardenArgs, "--debug-bind-port", fmt.Sprintf("%d", 8080+ginkgo.GinkgoParallelNode()))

	return exec.Command(bin, gardenArgs...)
}

func (r *RunningGarden) Cleanup() {
	// unmount aufs since the docker graph driver leaves this around,
	// otherwise the following commands might fail
	retry := retrier.New(retrier.ConstantBackoff(200, 500*time.Millisecond), nil)

	err := retry.Run(func() error {
		if err := os.RemoveAll(path.Join(r.GraphPath, "aufs")); err == nil {
			return nil // if we can remove it, it's already unmounted
		}

		if err := syscall.Unmount(path.Join(r.GraphPath, "aufs"), MNT_DETACH); err != nil {
			r.logger.Error("failed-unmount-attempt", err)
			return err
		}

		return nil
	})

	if err != nil {
		r.logger.Error("failed-to-unmount", err)
	}

	MustUnmountTmpfs(r.GraphPath)

	// In the kernel version 3.19.0-51-generic the code bellow results in
	// hanging the running VM. We are not deleting the node-X directories. They
	// are empty and the next test will re-use them. We will stick with that
	// workaround until we can test on a newer kernel that will hopefully not
	// have this bug.
	//
	// if err := os.RemoveAll(r.GraphPath); err != nil {
	// 	r.logger.Error("remove-graph", err)
	// }

	r.logger.Info("cleanup-tempdirs")
	if err := os.RemoveAll(r.Tmpdir); err != nil {
		r.logger.Error("cleanup-tempdirs-failed", err, lager.Data{"tmpdir": r.Tmpdir})
	} else {
		r.logger.Info("tempdirs-removed")
	}
}

func (r *RunningGarden) DestroyContainers() error {
	containers, err := r.Containers(nil)
	if err != nil {
		return err
	}

	for _, container := range containers {
		r.Destroy(container.Handle())
	}

	return nil
}

type debugVars struct {
	NumGoRoutines int `json:"numGoRoutines"`
}

func (r *RunningGarden) NumGoroutines() (int, error) {
	debugURL := fmt.Sprintf("http://%s:%d/debug/vars", r.debugIP, r.debugPort)
	res, err := http.Get(debugURL)
	defer res.Body.Close()
	if err != nil {
		return 0, err
	}

	decoder := json.NewDecoder(res.Body)
	var debugVarsData debugVars
	err = decoder.Decode(&debugVarsData)
	if err != nil {
		return 0, err
	}

	return debugVarsData.NumGoRoutines, nil
}

func (r *RunningGarden) Buffer() *gbytes.Buffer {
	return r.runner.Buffer()
}

func (r *RunningGarden) ExitCode() int {
	return r.runner.ExitCode()
}
<|endoftext|>


<commit_before>package gardenrunner

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/cloudfoundry-incubator/garden/client"
	"github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/onsi/ginkgo"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/lager/lagertest"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

type Runner struct {
	Command *exec.Cmd

	network string
	addr    string

	bin  string
	argv []string

	binPath    string
	rootFSPath string

	tmpdir    string
	graphRoot string
	graphPath string
}

func New(network, addr string, bin, binPath, rootFSPath, graphRoot string, argv ...string) *Runner {
	tmpDir := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()),
	)

	if graphRoot == "" {
		graphRoot = filepath.Join(tmpDir, "graph")
	}

	graphPath := filepath.Join(graphRoot, fmt.Sprintf("node-%d", ginkgo.GinkgoParallelNode()))

	return &Runner{
		network: network,
		addr:    addr,

		bin:  bin,
		argv: argv,

		binPath:    binPath,
		rootFSPath: rootFSPath,
		graphRoot:  graphRoot,
		graphPath:  graphPath,
		tmpdir:     tmpDir,
	}
}

func (r *Runner) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	logger := lagertest.NewTestLogger("garden-runner")

	if err := os.MkdirAll(r.tmpdir, 0755); err != nil {
		return err
	}

	depotPath := filepath.Join(r.tmpdir, "containers")
	snapshotsPath := filepath.Join(r.tmpdir, "snapshots")

	if err := os.MkdirAll(depotPath, 0755); err != nil {
		return err
	}

	if err := os.MkdirAll(snapshotsPath, 0755); err != nil {
		return err
	}

	var appendDefaultFlag = func(ar []string, key, value string) []string {
		for _, a := range r.argv {
			if a == key {
				return ar
			}
		}

		if value != "" {
			return append(ar, key, value)
		} else {
			return append(ar, key)
		}
	}

	var hasFlag = func(ar []string, key string) bool {
		for _, a := range ar {
			if a == key {
				return true
			}
		}

		return false
	}

	gardenArgs := make([]string, len(r.argv))
	copy(gardenArgs, r.argv)

	gardenArgs = appendDefaultFlag(gardenArgs, "--listenNetwork", r.network)
	gardenArgs = appendDefaultFlag(gardenArgs, "--listenAddr", r.addr)
	gardenArgs = appendDefaultFlag(gardenArgs, "--bin", r.binPath)
	if r.rootFSPath != "" { //rootfs is an optional parameter
		gardenArgs = appendDefaultFlag(gardenArgs, "--rootfs", r.rootFSPath)
	}
	gardenArgs = appendDefaultFlag(gardenArgs, "--depot", depotPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--snapshots", snapshotsPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--graph", r.graphPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--logLevel", "debug")
	gardenArgs = appendDefaultFlag(gardenArgs, "--networkPool", fmt.Sprintf("10.250.%d.0/24", ginkgo.GinkgoParallelNode()))
	gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolStart", strconv.Itoa(51000+(1000*ginkgo.GinkgoParallelNode())))
	gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolSize", "1000")
	gardenArgs = appendDefaultFlag(gardenArgs, "--tag", strconv.Itoa(ginkgo.GinkgoParallelNode()))

	btrfsIsSupported := strings.EqualFold(os.Getenv("BTRFS_SUPPORTED"), "true")
	hasDisabledFlag := hasFlag(gardenArgs, "-disableQuotas=true")

	if !btrfsIsSupported && !hasDisabledFlag {
		// We should disabled quotas if BTRFS is not supported
		gardenArgs = appendDefaultFlag(gardenArgs, "--disableQuotas", "")
	}

	gardenArgs = appendDefaultFlag(gardenArgs, "--debugAddr", fmt.Sprintf(":808%d", ginkgo.GinkgoParallelNode()))

	var signal os.Signal

	r.Command = exec.Command(r.bin, gardenArgs...)

	process := ifrit.Invoke(&ginkgomon.Runner{
		Name:              "garden-linux",
		Command:           r.Command,
		AnsiColorCode:     "31m",
		StartCheck:        "garden-linux.started",
		StartCheckTimeout: 30 * time.Second,
		Cleanup: func() {
			if signal == syscall.SIGQUIT {
				logger.Info("cleanup-subvolumes")

				// remove contents of subvolumes before deleting the subvolume
				if err := os.RemoveAll(r.graphPath); err != nil {
					logger.Error("remove graph", err)
				}

				if btrfsIsSupported {
					// need to remove subvolumes before cleaning graphpath
					subvolumesOutput, err := exec.Command("btrfs", "subvolume", "list", r.graphRoot).CombinedOutput()
					logger.Debug(fmt.Sprintf("listing-subvolumes: %s", string(subvolumesOutput)))
					if err != nil {
						logger.Fatal("listing-subvolumes-error", err)
					}
					for _, line := range strings.Split(string(subvolumesOutput), "\n") {
						fields := strings.Fields(line)
						if len(fields) < 1 {
							continue
						}
						subvolumeRelativePath := fields[len(fields)-1]
						subvolumeAbsolutePath := filepath.Join(r.graphRoot, subvolumeRelativePath)
						if strings.Contains(subvolumeAbsolutePath, r.graphPath) {
							if b, err := exec.Command("btrfs", "subvolume", "delete", subvolumeAbsolutePath).CombinedOutput(); err != nil {
								logger.Fatal(fmt.Sprintf("deleting-subvolume: %s", string(b)), err)
							}
						}
					}

					if err := os.RemoveAll(r.graphPath); err != nil {
						logger.Error("remove graph again", err)
					}
				}

				logger.Info("cleanup-tempdirs")
				if err := os.RemoveAll(r.tmpdir); err != nil {
					logger.Error("cleanup-tempdirs-failed", err, lager.Data{"tmpdir": r.tmpdir})
				} else {
					logger.Info("tempdirs-removed")
				}
			}
		},
	})

	close(ready)

	for {
		select {
		case signal = <-signals:
			// SIGQUIT means clean up the containers, the garden process (SIGTERM) and the temporary directories
			// SIGKILL, SIGTERM and SIGINT are passed through to the garden process
			if signal == syscall.SIGQUIT {
				logger.Info("received-signal SIGQUIT")
				if err := r.destroyContainers(); err != nil {
					logger.Error("destroy-containers-failed", err)
					return err
				}
				logger.Info("destroyed-containers")
				process.Signal(syscall.SIGTERM)
			} else {
				logger.Info("received-signal", lager.Data{"signal": signal})
				process.Signal(signal)
			}

		case waitErr := <-process.Wait():
			logger.Info("process-exited")
			return waitErr
		}
	}
}

func (r *Runner) TryDial() error {
	conn, dialErr := net.DialTimeout(r.network, r.addr, 100*time.Millisecond)

	if dialErr == nil {
		conn.Close()
		return nil
	}

	return dialErr
}

func (r *Runner) NewClient() client.Client {
	return client.New(connection.New(r.network, r.addr))
}

func (r *Runner) destroyContainers() error {
	client := r.NewClient()

	containers, err := client.Containers(nil)
	if err != nil {
		return err
	}

	for _, container := range containers {
		err := client.Destroy(container.Handle())
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Revert "Revert "Remove support for btrfs""<commit_after>package gardenrunner

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"syscall"
	"time"

	"github.com/cloudfoundry-incubator/garden/client"
	"github.com/cloudfoundry-incubator/garden/client/connection"
	"github.com/onsi/ginkgo"
	"github.com/pivotal-golang/lager"
	"github.com/pivotal-golang/lager/lagertest"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

type Runner struct {
	Command *exec.Cmd

	network string
	addr    string

	bin  string
	argv []string

	binPath    string
	rootFSPath string

	tmpdir    string
	graphRoot string
	graphPath string
}

func New(network, addr string, bin, binPath, rootFSPath, graphRoot string, argv ...string) *Runner {
	tmpDir := filepath.Join(
		os.TempDir(),
		fmt.Sprintf("test-garden-%d", ginkgo.GinkgoParallelNode()),
	)

	if graphRoot == "" {
		graphRoot = filepath.Join(tmpDir, "graph")
	}

	graphPath := filepath.Join(graphRoot, fmt.Sprintf("node-%d", ginkgo.GinkgoParallelNode()))

	return &Runner{
		network: network,
		addr:    addr,

		bin:  bin,
		argv: argv,

		binPath:    binPath,
		rootFSPath: rootFSPath,
		graphRoot:  graphRoot,
		graphPath:  graphPath,
		tmpdir:     tmpDir,
	}
}

func (r *Runner) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	logger := lagertest.NewTestLogger("garden-runner")

	if err := os.MkdirAll(r.tmpdir, 0755); err != nil {
		return err
	}

	depotPath := filepath.Join(r.tmpdir, "containers")
	snapshotsPath := filepath.Join(r.tmpdir, "snapshots")

	if err := os.MkdirAll(depotPath, 0755); err != nil {
		return err
	}

	if err := os.MkdirAll(snapshotsPath, 0755); err != nil {
		return err
	}

	var appendDefaultFlag = func(ar []string, key, value string) []string {
		for _, a := range r.argv {
			if a == key {
				return ar
			}
		}

		if value != "" {
			return append(ar, key, value)
		} else {
			return append(ar, key)
		}
	}

	gardenArgs := make([]string, len(r.argv))
	copy(gardenArgs, r.argv)

	gardenArgs = appendDefaultFlag(gardenArgs, "--listenNetwork", r.network)
	gardenArgs = appendDefaultFlag(gardenArgs, "--listenAddr", r.addr)
	gardenArgs = appendDefaultFlag(gardenArgs, "--bin", r.binPath)
	if r.rootFSPath != "" { //rootfs is an optional parameter
		gardenArgs = appendDefaultFlag(gardenArgs, "--rootfs", r.rootFSPath)
	}
	gardenArgs = appendDefaultFlag(gardenArgs, "--depot", depotPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--snapshots", snapshotsPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--graph", r.graphPath)
	gardenArgs = appendDefaultFlag(gardenArgs, "--logLevel", "debug")
	gardenArgs = appendDefaultFlag(gardenArgs, "--networkPool", fmt.Sprintf("10.250.%d.0/24", ginkgo.GinkgoParallelNode()))
	gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolStart", strconv.Itoa(51000+(1000*ginkgo.GinkgoParallelNode())))
	gardenArgs = appendDefaultFlag(gardenArgs, "--portPoolSize", "1000")
	gardenArgs = appendDefaultFlag(gardenArgs, "--tag", strconv.Itoa(ginkgo.GinkgoParallelNode()))

	gardenArgs = appendDefaultFlag(gardenArgs, "--debugAddr", fmt.Sprintf(":808%d", ginkgo.GinkgoParallelNode()))

	var signal os.Signal

	r.Command = exec.Command(r.bin, gardenArgs...)

	process := ifrit.Invoke(&ginkgomon.Runner{
		Name:              "garden-linux",
		Command:           r.Command,
		AnsiColorCode:     "31m",
		StartCheck:        "garden-linux.started",
		StartCheckTimeout: 30 * time.Second,
		Cleanup: func() {
			if signal == syscall.SIGQUIT {
				logger.Info("cleanup-subvolumes")

				// remove contents of subvolumes before deleting the subvolume
				if err := os.RemoveAll(r.graphPath); err != nil {
					logger.Error("remove graph", err)
				}

				logger.Info("cleanup-tempdirs")
				if err := os.RemoveAll(r.tmpdir); err != nil {
					logger.Error("cleanup-tempdirs-failed", err, lager.Data{"tmpdir": r.tmpdir})
				} else {
					logger.Info("tempdirs-removed")
				}
			}
		},
	})

	close(ready)

	for {
		select {
		case signal = <-signals:
			// SIGQUIT means clean up the containers, the garden process (SIGTERM) and the temporary directories
			// SIGKILL, SIGTERM and SIGINT are passed through to the garden process
			if signal == syscall.SIGQUIT {
				logger.Info("received-signal SIGQUIT")
				if err := r.destroyContainers(); err != nil {
					logger.Error("destroy-containers-failed", err)
					return err
				}
				logger.Info("destroyed-containers")
				process.Signal(syscall.SIGTERM)
			} else {
				logger.Info("received-signal", lager.Data{"signal": signal})
				process.Signal(signal)
			}

		case waitErr := <-process.Wait():
			logger.Info("process-exited")
			return waitErr
		}
	}
}

func (r *Runner) TryDial() error {
	conn, dialErr := net.DialTimeout(r.network, r.addr, 100*time.Millisecond)

	if dialErr == nil {
		conn.Close()
		return nil
	}

	return dialErr
}

func (r *Runner) NewClient() client.Client {
	return client.New(connection.New(r.network, r.addr))
}

func (r *Runner) destroyContainers() error {
	client := r.NewClient()

	containers, err := client.Containers(nil)
	if err != nil {
		return err
	}

	for _, container := range containers {
		err := client.Destroy(container.Handle())
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package graphemes provides a scanner for Unicode text segmentation grapheme cluster boundaries: https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
package graphemes

import (
	"bufio"
	"bytes"
	"io"
	"unicode"

	"github.com/clipperhouse/uax29/emoji"
)

// NewScanner tokenizes a reader into a stream of grapheme clusters according to Unicode Text Segmentation boundaries https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
// Iterate through the stream by calling Scan() until false.
//	text := "Good dog! 👍🏼🐶"
//	reader := strings.NewReader(text)
//
//	scanner := graphemes.NewScanner(reader)
//	for scanner.Scan() {
//		fmt.Printf("%s\n", scanner.Text())
//	}
//	if err := scanner.Err(); err != nil {
//		log.Fatal(err)
//	}
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		incoming: bufio.NewReaderSize(r, 64*1024),
	}
}

// Scanner is the structure for scanning an input Reader. Use NewScanner to instantiate.
type Scanner struct {
	incoming *bufio.Reader

	// a buffer of runes to evaluate
	buffer []rune
	// a cursor for runes in the buffer
	pos int

	bb bytes.Buffer

	// outputs
	bytes []byte
	err   error
}

// reset creates a new bytes.Buffer on the Scanner, and clears previous values
func (sc *Scanner) reset() {
	// Drop the emitted runes (optimization to avoid growing array)
	copy(sc.buffer, sc.buffer[sc.pos:])
	sc.buffer = sc.buffer[:len(sc.buffer)-sc.pos]

	sc.pos = 0

	var bb bytes.Buffer
	sc.bb = bb

	sc.bytes = nil
	sc.err = nil
}

// Scan advances to the next token, returning true if successful. Returns false on error or EOF.
func (sc *Scanner) Scan() bool {
	sc.reset()

	for {
		// Fill the buffer with enough runes for lookahead
		for len(sc.buffer) < sc.pos+8 {
			r, eof, err := sc.readRune()
			if err != nil {
				sc.err = err
				return false
			}
			if eof {
				break
			}
			sc.buffer = append(sc.buffer, r)
		}

		switch {
		case sc.gb1():
			// true indicates continue
			sc.accept()
			continue
		case sc.gb2():
			// true indicates break
			break
		case
			sc.gb3():
			// true indicates continue
			sc.accept()
			continue
		case
			sc.gb4(),
			sc.gb5():
			// true indicates break
			break
		case
			sc.gb6(),
			sc.gb7(),
			sc.gb8(),
			sc.gb9(),
			sc.gb9a(),
			sc.gb9b(),
			sc.gb11(),
			sc.gb12(),
			sc.gb13():
			// true indicates continue
			sc.accept()
			continue
		}

		// If we fall through all the above rules, it's a break
		break
	}

	return sc.gb999()
}

// Bytes returns the current token as a byte slice, after a successful call to Scan
func (sc *Scanner) Bytes() []byte {
	return sc.bytes
}

// Text returns the current token, after a successful call to Scan
func (sc *Scanner) Text() string {
	return string(sc.bytes)
}

// Err returns the current error, after an unsuccessful call to Scan
func (sc *Scanner) Err() error {
	return sc.err
}

// Grapheme cluster rules: https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
// In most cases, returning true means 'keep going'; check the name of the return var for clarity

var is = unicode.Is

// gb1 implements https://unicode.org/reports/tr29/#GB1
func (sc *Scanner) gb1() (accept bool) {
	sot := sc.pos == 0 // "start of text"
	eof := len(sc.buffer) == sc.pos
	return sot && !eof
}

// gb2 implements https://unicode.org/reports/tr29/#GB2
func (sc *Scanner) gb2() (breaking bool) {
	// eof
	return len(sc.buffer) == sc.pos
}

// gb3 implements https://unicode.org/reports/tr29/#GB3
func (sc *Scanner) gb3() (accept bool) {
	current := sc.buffer[sc.pos]
	if !is(LF, current) {
		return false
	}

	previous := sc.buffer[sc.pos-1]
	return is(CR, previous)
}

// gb4 implements https://unicode.org/reports/tr29/#GB4
func (sc *Scanner) gb4() (breaking bool) {
	previous := sc.buffer[sc.pos-1]
	return is(_mergedControlCRLF, previous)
}

// gb5 implements https://unicode.org/reports/tr29/#GB5
func (sc *Scanner) gb5() (breaking bool) {
	current := sc.buffer[sc.pos]
	return is(_mergedControlCRLF, current)
}

// gb6 implements https://unicode.org/reports/tr29/#GB6
func (sc *Scanner) gb6() (accept bool) {
	if sc.pos < 1 {
		return false
	}

	current := sc.buffer[sc.pos]
	if !is(_mergedLVLVLVT, current) {
		return false
	}

	previous := sc.buffer[sc.pos-1]
	return is(L, previous)
}

// gb7 implements https://unicode.org/reports/tr29/#GB7
func (sc *Scanner) gb7() (accept bool) {
	if sc.pos < 1 {
		return false
	}

	current := sc.buffer[sc.pos]
	if !is(_mergedVT, current) {
		return false
	}

	previous := sc.buffer[sc.pos-1]
	return is(_mergedLVV, previous)
}

// gb8 implements https://unicode.org/reports/tr29/#GB8
func (sc *Scanner) gb8() (accept bool) {
	if sc.pos < 1 {
		return false
	}

	current := sc.buffer[sc.pos]
	if !is(T, current) {
		return false
	}

	previous := sc.buffer[sc.pos-1]
	return is(_mergedLVTT, previous)
}

// gb9 implements https://unicode.org/reports/tr29/#GB9
func (sc *Scanner) gb9() (accept bool) {
	current := sc.buffer[sc.pos]
	return is(_mergedExtendZWJ, current)
}

// gb9 implements https://unicode.org/reports/tr29/#GB9a
func (sc *Scanner) gb9a() (accept bool) {
	current := sc.buffer[sc.pos]
	return is(SpacingMark, current)
}

// gb9 implements https://unicode.org/reports/tr29/#GB9b
func (sc *Scanner) gb9b() (accept bool) {
	if sc.pos < 1 {
		return false
	}

	previous := sc.buffer[sc.pos-1]
	return is(Prepend, previous)
}

// seekPreviousIndex works backward until it hits a rune satisfying one of the range tables,
// ignoring Extend, and returns the index of the rune in the buffer
// See: https://unicode.org/reports/tr29/#Grapheme_Cluster_and_Format_Rules (driven by SB5)
func (sc *Scanner) seekPreviousIndex(pos int, rts ...*unicode.RangeTable) int {
	// Start at the end of the buffer and move backwards
	for i := pos - 1; i >= 0; i-- {
		r := sc.buffer[i]

		// Ignore Extend
		if is(Extend, r) {
			continue
		}

		// See if any of the range tables apply
		for _, rt := range rts {
			if is(rt, r) {
				return i
			}
		}

		// If we get this far, it's not there
		break
	}

	return -1
}

// seekPreviousIndex works backward ahead until it hits a rune satisfying one of the range tables,
// ignoring Extend|Format, reporting success
// Logic is here: https://unicode.org/reports/tr29/#Grapheme_Cluster_and_Format_Rules (driven by SB5)
func (sc *Scanner) seekPrevious(pos int, rts ...*unicode.RangeTable) bool {
	return sc.seekPreviousIndex(pos, rts...) >= 0
}

// gb11 implements https://unicode.org/reports/tr29/#GB11
func (sc *Scanner) gb11() (accept bool) {
	if sc.pos < 2 {
		return false
	}

	current := sc.buffer[sc.pos]
	if !is(emoji.Extended_Pictographic, current) {
		return false
	}

	previous := sc.buffer[sc.pos-1]
	if !is(ZWJ, previous) {
		return false
	}

	return sc.seekPrevious(sc.pos-1, emoji.Extended_Pictographic)
}

// gb12 implements https://unicode.org/reports/tr29/#GB12
func (sc *Scanner) gb12() (accept bool) {
	current := sc.buffer[sc.pos]
	if !is(Regional_Indicator, current) {
		return false
	}

	// Buffer comprised entirely of an odd number of RI
	count := 0
	for i := sc.pos - 1; i >= 0; i-- {
		r := sc.buffer[i]
		if !is(Regional_Indicator, r) {
			return false
		}
		count++
	}

	// If we fall through, we've seen the whole buffer,
	// so it's all Regional_Indicator
	odd := count > 0 && count%2 == 1
	return odd
}

// gb13 implements https://unicode.org/reports/tr29/#GB13
func (sc *Scanner) gb13() (accept bool) {
	current := sc.buffer[sc.pos]
	if !is(Regional_Indicator, current) {
		return false
	}

	// Last n runes represent an odd number of RI
	count := 0
	for i := sc.pos - 1; i >= 0; i-- {
		r := sc.buffer[i]
		if !is(Regional_Indicator, r) {
			odd := count > 0 && count%2 == 1
			return odd
		}
		count++
	}

	return false
}

// gb999 implements https://unicode.org/reports/tr29/#GB999
// i.e. break
func (sc *Scanner) gb999() bool {
	sc.bytes = sc.bb.Bytes()
	return len(sc.bytes) > 0
}

// accept forwards the buffer cursor (pos) by 1
func (sc *Scanner) accept() {
	sc.bb.WriteRune(sc.buffer[sc.pos])
	sc.pos++
}

// readRune gets the next rune, advancing the reader
func (sc *Scanner) readRune() (r rune, eof bool, err error) {
	r, _, err = sc.incoming.ReadRune()

	if err != nil {
		if err == io.EOF {
			return r, true, nil
		}
		return r, false, err
	}

	return r, false, nil
}
<commit_msg>Inline grapheme rules, small speedup<commit_after>// Package graphemes provides a scanner for Unicode text segmentation grapheme cluster boundaries: https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
package graphemes

import (
	"bufio"
	"bytes"
	"io"
	"unicode"

	"github.com/clipperhouse/uax29/emoji"
)

// NewScanner tokenizes a reader into a stream of grapheme clusters according to Unicode Text Segmentation boundaries https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
// Iterate through the stream by calling Scan() until false.
//	text := "Good dog! 👍🏼🐶"
//	reader := strings.NewReader(text)
//
//	scanner := graphemes.NewScanner(reader)
//	for scanner.Scan() {
//		fmt.Printf("%s\n", scanner.Text())
//	}
//	if err := scanner.Err(); err != nil {
//		log.Fatal(err)
//	}
func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		incoming: bufio.NewReaderSize(r, 64*1024),
	}
}

// Scanner is the structure for scanning an input Reader. Use NewScanner to instantiate.
type Scanner struct {
	incoming *bufio.Reader

	// a buffer of runes to evaluate
	buffer []rune
	// a cursor for runes in the buffer
	pos int

	bb bytes.Buffer

	// outputs
	bytes []byte
	err   error
}

// reset creates a new bytes.Buffer on the Scanner, and clears previous values
func (sc *Scanner) reset() {
	// Drop the emitted runes (optimization to avoid growing array)
	copy(sc.buffer, sc.buffer[sc.pos:])
	sc.buffer = sc.buffer[:len(sc.buffer)-sc.pos]

	sc.pos = 0

	var bb bytes.Buffer
	sc.bb = bb

	sc.bytes = nil
	sc.err = nil
}

// Scan advances to the next token, returning true if successful. Returns false on error or EOF.
func (sc *Scanner) Scan() bool {
	sc.reset()

	for {
		// Fill the buffer with enough runes for lookahead
		for len(sc.buffer) < sc.pos+8 {
			r, eof, err := sc.readRune()
			if err != nil {
				sc.err = err
				return false
			}
			if eof {
				break
			}
			sc.buffer = append(sc.buffer, r)
		}

		sot := sc.pos == 0 // "start of text"
		eof := len(sc.buffer) == sc.pos

		// GB1
		if sot && !eof {
			sc.accept()
			continue
		}

		// GB2
		if eof {
			break
		}

		current := sc.buffer[sc.pos]
		previous := sc.buffer[sc.pos-1]

		// GB3
		if is(LF, current) && is(CR, previous) {
			sc.accept()
			continue
		}

		// GB4
		if is(_mergedControlCRLF, previous) {
			break
		}

		// GB5
		if is(_mergedControlCRLF, current) {
			break
		}

		// GB6
		if is(_mergedLVLVLVT, current) && is(L, previous) {
			sc.accept()
			continue
		}

		// GB7
		if is(_mergedVT, current) && is(_mergedLVV, previous) {
			sc.accept()
			continue
		}

		// GB8
		if is(T, current) && is(_mergedLVTT, previous) {
			sc.accept()
			continue
		}

		// GB9
		if is(_mergedExtendZWJ, current) {
			sc.accept()
			continue
		}

		// GB9a
		if is(SpacingMark, current) {
			sc.accept()
			continue
		}

		// GB9b
		if is(Prepend, previous) {
			sc.accept()
			continue
		}

		// GB11
		if is(emoji.Extended_Pictographic, current) && is(ZWJ, previous) && sc.seekPrevious(sc.pos-1, emoji.Extended_Pictographic) {
			sc.accept()
			continue
		}

		// GB12
		if is(Regional_Indicator, current) {
			// Buffer comprised entirely of an odd number of RI
			ok := true
			count := 0
			for i := sc.pos - 1; i >= 0; i-- {
				r := sc.buffer[i]
				if !is(Regional_Indicator, r) {
					ok = false
				}
				count++
			}

			if ok {
				// If we fall through, we've seen the whole buffer,
				// so it's all Regional_Indicator
				odd := count > 0 && count%2 == 1
				if odd {
					sc.accept()
					continue
				}
			}
		}

		// GB13
		if is(Regional_Indicator, current) {
			// Last n runes represent an odd number of RI
			ok := false
			count := 0
			for i := sc.pos - 1; i >= 0; i-- {
				r := sc.buffer[i]
				if !is(Regional_Indicator, r) {
					odd := count > 0 && count%2 == 1
					ok = odd
					break
				}
				count++
			}

			if ok {
				sc.accept()
				continue
			}
		}

		// WB999
		// If we fall through all the above rules, it's a break
		break
	}

	return sc.token()
}

// Bytes returns the current token as a byte slice, after a successful call to Scan
func (sc *Scanner) Bytes() []byte {
	return sc.bytes
}

// Text returns the current token, after a successful call to Scan
func (sc *Scanner) Text() string {
	return string(sc.bytes)
}

// Err returns the current error, after an unsuccessful call to Scan
func (sc *Scanner) Err() error {
	return sc.err
}

// Grapheme cluster rules: https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
// In most cases, returning true means 'keep going'; check the name of the return var for clarity

var is = unicode.Is

// seekPreviousIndex works backward until it hits a rune satisfying one of the range tables,
// ignoring Extend, and returns the index of the rune in the buffer
// See: https://unicode.org/reports/tr29/#Grapheme_Cluster_and_Format_Rules (driven by SB5)
func (sc *Scanner) seekPreviousIndex(pos int, rts ...*unicode.RangeTable) int {
	// Start at the end of the buffer and move backwards
	for i := pos - 1; i >= 0; i-- {
		r := sc.buffer[i]

		// Ignore Extend
		if is(Extend, r) {
			continue
		}

		// See if any of the range tables apply
		for _, rt := range rts {
			if is(rt, r) {
				return i
			}
		}

		// If we get this far, it's not there
		break
	}

	return -1
}

// seekPreviousIndex works backward ahead until it hits a rune satisfying one of the range tables,
// ignoring Extend|Format, reporting success
// Logic is here: https://unicode.org/reports/tr29/#Grapheme_Cluster_and_Format_Rules (driven by SB5)
func (sc *Scanner) seekPrevious(pos int, rts ...*unicode.RangeTable) bool {
	return sc.seekPreviousIndex(pos, rts...) >= 0
}

// gb999 implements https://unicode.org/reports/tr29/#GB999
// i.e. break
func (sc *Scanner) token() bool {
	sc.bytes = sc.bb.Bytes()
	return len(sc.bytes) > 0
}

// accept forwards the buffer cursor (pos) by 1
func (sc *Scanner) accept() {
	sc.bb.WriteRune(sc.buffer[sc.pos])
	sc.pos++
}

// readRune gets the next rune, advancing the reader
func (sc *Scanner) readRune() (r rune, eof bool, err error) {
	r, _, err = sc.incoming.ReadRune()

	if err != nil {
		if err == io.EOF {
			return r, true, nil
		}
		return r, false, err
	}

	return r, false, nil
}
<|endoftext|>


<commit_before>	//Package expect lets you write better assertions
	package expect

	import (
"reflect"
"testing"
)

	//Info store the value and the expect methods
	type Info struct {
		value interface{}
	}

	//TheValue recive the data to use for assurance
	func TheValue(i interface{}) Info {
		return Info{value: i}
	}

	//ToBe asserts that object is strictly equal to value.
	func (i Info) ToBe(theExpectedValue interface{}) func(t *testing.T) {
		return func(t *testing.T) {
			valueType := reflect.TypeOf(i.value).Kind()
			theExpectedValueType := reflect.TypeOf(theExpectedValue).Kind()

			if valueType != theExpectedValueType {
				t.Errorf("The expected value is a %v and the value is a %v", theExpectedValueType, valueType)
			} else {
				switch valueType {
				case reflect.Slice:
					s := reflect.ValueOf(theExpectedValue)
					value := reflect.ValueOf(i.value)

					for j := 0; j < s.Len(); j++ {
						if s.Index(j).Interface() != value.Index(j).Interface() {
							t.Errorf("Expected the value %v to be %v", i.value, theExpectedValue)
							break
						}
					}
				default:
					if i.value != theExpectedValue {
						t.Errorf("Expected the value %v to be %v", i.value, theExpectedValue)
					}
				}
			}
		}
	}

	//ToNotBe asserts that object is not strictly equal to value.
	func (i Info) ToNotBe(theNotExpectedValue interface{}) func(t *testing.T) {
		return func(t *testing.T) {
			valueType := reflect.TypeOf(i.value).Kind()
			theNotExpectedValueType := reflect.TypeOf(theNotExpectedValue).Kind()

			if valueType != theNotExpectedValueType {
				t.Errorf("The not expected value is a %v and the value is a %v", theNotExpectedValueType, valueType)
			} else {
				switch valueType {
				case reflect.Slice:
					s := reflect.ValueOf(theNotExpectedValue)
					value := reflect.ValueOf(i.value)

					hasSomeValueDifferent := false
					for j := 0; j < s.Len(); j++ {
						if s.Index(j).Interface() != value.Index(j).Interface() {
							hasSomeValueDifferent = true
							break
						}
					}

					if !hasSomeValueDifferent {
						t.Errorf("Expected the value %v to not be %v", i.value, theNotExpectedValue)
					}
				default:
					if i.value == theNotExpectedValue {
						t.Errorf("Expected the value %v to not be %v", i.value, theNotExpectedValue)
					}
				}
			}
		}
	}

	//ToBeLessThanOrEqualTo asserts the given number is less than or equal to value.
	func (i Info) ToBeLessThanOrEqualTo(theNotExpectedValue int) func(t *testing.T) {
		return func(t *testing.T) {
			value, ok := i.value.(int)
			if ok {
				if value > theNotExpectedValue {
					t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
				}
			} else {
				t.Errorf("The parameter %v is not of type int", i.value)
			}
		}
	}

	//ToBeLessThan asserts the given number is less than the value.
	func (i Info) ToBeLessThan(theNotExpectedValue int) func(t *testing.T) {
		return func(t *testing.T) {
			value, ok := i.value.(int)
			if ok {
				if value >= theNotExpectedValue {
					t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
				}
			} else {
				t.Errorf("The parameter %v is not of type int", i.value)
			}
		}
	}

	//ToBeGreaterThanOrEqualTo asserts the given number is greater than or equal to value.
	func (i Info) ToBeGreaterThanOrEqualTo(theNotExpectedValue int) func(t *testing.T) {
		return func(t *testing.T) {
			value, ok := i.value.(int)
			if ok {
				if value < theNotExpectedValue {
					t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
				}
			} else {
				t.Errorf("The parameter %v is not of type int", i.value)
			}
		}
	}

	//ToBeGreaterThan asserts the given number is greater than the value.
	func (i Info) ToBeGreaterThan(theNotExpectedValue int) func(t *testing.T) {
		return func(t *testing.T) {
			value, ok := i.value.(int)
			if ok {
				if value <= theNotExpectedValue {
					t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
				}
			} else {
				t.Errorf("The parameter %v is not of type int", i.value)
			}
		}
	}

	//ToExist asserts the given object is not nil.
	func (i Info) ToExist() func(t *testing.T) {
		return func(t *testing.T) {
			if i.value == nil {
				t.Errorf("Expected to exist but got %v", i.value)
			}
		}
	}

	//ToNotExist asserts the given object is nil.
	func (i Info) ToNotExist() func(t *testing.T) {
		return func(t *testing.T) {
			if i.value != nil {
				t.Errorf("Expected to not exist but got %v", i.value)
			}
		}
	}

	//Contains asserts the given array contains the informed value
	func (i Info) Contains(theExpectedValue interface{}) func(t *testing.T) {
		return func(t *testing.T) {

			valueType := reflect.TypeOf(i.value).Kind()
			theExpectedValueType := reflect.TypeOf(theExpectedValue).Kind()

			value := reflect.ValueOf(i.value)

			if valueType != reflect.Slice {
				t.Errorf("The value infromed is of type %v", valueType)
			} else if value.Len() == 0 {
				t.Errorf("The value informed is an empty slice")
			} else {

				sliceType := reflect.TypeOf(value.Index(0).Interface()).Kind()

				if sliceType !=  theExpectedValueType {
					t.Errorf("The expected value informed is of type %v and the slice is of type %v", theExpectedValueType, sliceType)
				} else {

					found := false

					for j := 0; j < value.Len(); j++ {
						if value.Index(j).Interface() == theExpectedValue {
							found = true
							break
						}
					}

					if !found {
						t.Errorf("Informed value %v not found in slice %v", theExpectedValue,  i.value)
					}
				}
			}
		}
	}<commit_msg>Indent<commit_after>//Package expect lets you write better assertions
package expect

import (
	"reflect"
	"testing"
)

//Info store the value and the expect methods
type Info struct {
	value interface{}
}

//TheValue recive the data to use for assurance
func TheValue(i interface{}) Info {
	return Info{value: i}
}

//ToBe asserts that object is strictly equal to value.
func (i Info) ToBe(theExpectedValue interface{}) func(t *testing.T) {
	return func(t *testing.T) {
		valueType := reflect.TypeOf(i.value).Kind()
		theExpectedValueType := reflect.TypeOf(theExpectedValue).Kind()

		if valueType != theExpectedValueType {
			t.Errorf("The expected value is a %v and the value is a %v", theExpectedValueType, valueType)
		} else {
			switch valueType {
			case reflect.Slice:
				s := reflect.ValueOf(theExpectedValue)
				value := reflect.ValueOf(i.value)

				for j := 0; j < s.Len(); j++ {
					if s.Index(j).Interface() != value.Index(j).Interface() {
						t.Errorf("Expected the value %v to be %v", i.value, theExpectedValue)
						break
					}
				}
			default:
				if i.value != theExpectedValue {
					t.Errorf("Expected the value %v to be %v", i.value, theExpectedValue)
				}
			}
		}
	}
}

//ToNotBe asserts that object is not strictly equal to value.
func (i Info) ToNotBe(theNotExpectedValue interface{}) func(t *testing.T) {
	return func(t *testing.T) {
		valueType := reflect.TypeOf(i.value).Kind()
		theNotExpectedValueType := reflect.TypeOf(theNotExpectedValue).Kind()

		if valueType != theNotExpectedValueType {
			t.Errorf("The not expected value is a %v and the value is a %v", theNotExpectedValueType, valueType)
		} else {
			switch valueType {
			case reflect.Slice:
				s := reflect.ValueOf(theNotExpectedValue)
				value := reflect.ValueOf(i.value)

				hasSomeValueDifferent := false
				for j := 0; j < s.Len(); j++ {
					if s.Index(j).Interface() != value.Index(j).Interface() {
						hasSomeValueDifferent = true
						break
					}
				}

				if !hasSomeValueDifferent {
					t.Errorf("Expected the value %v to not be %v", i.value, theNotExpectedValue)
				}
			default:
				if i.value == theNotExpectedValue {
					t.Errorf("Expected the value %v to not be %v", i.value, theNotExpectedValue)
				}
			}
		}
	}
}

//ToBeLessThanOrEqualTo asserts the given number is less than or equal to value.
func (i Info) ToBeLessThanOrEqualTo(theNotExpectedValue int) func(t *testing.T) {
	return func(t *testing.T) {
		value, ok := i.value.(int)
		if ok {
			if value > theNotExpectedValue {
				t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
			}
		} else {
			t.Errorf("The parameter %v is not of type int", i.value)
		}
	}
}

//ToBeLessThan asserts the given number is less than the value.
func (i Info) ToBeLessThan(theNotExpectedValue int) func(t *testing.T) {
	return func(t *testing.T) {
		value, ok := i.value.(int)
		if ok {
			if value >= theNotExpectedValue {
				t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
			}
		} else {
			t.Errorf("The parameter %v is not of type int", i.value)
		}
	}
}

//ToBeGreaterThanOrEqualTo asserts the given number is greater than or equal to value.
func (i Info) ToBeGreaterThanOrEqualTo(theNotExpectedValue int) func(t *testing.T) {
	return func(t *testing.T) {
		value, ok := i.value.(int)
		if ok {
			if value < theNotExpectedValue {
				t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
			}
		} else {
			t.Errorf("The parameter %v is not of type int", i.value)
		}
	}
}

//ToBeGreaterThan asserts the given number is greater than the value.
func (i Info) ToBeGreaterThan(theNotExpectedValue int) func(t *testing.T) {
	return func(t *testing.T) {
		value, ok := i.value.(int)
		if ok {
			if value <= theNotExpectedValue {
				t.Errorf("Expected the value %v to less than or equal to %v", i.value, theNotExpectedValue)
			}
		} else {
			t.Errorf("The parameter %v is not of type int", i.value)
		}
	}
}

//ToExist asserts the given object is not nil.
func (i Info) ToExist() func(t *testing.T) {
	return func(t *testing.T) {
		if i.value == nil {
			t.Errorf("Expected to exist but got %v", i.value)
		}
	}
}

//ToNotExist asserts the given object is nil.
func (i Info) ToNotExist() func(t *testing.T) {
	return func(t *testing.T) {
		if i.value != nil {
			t.Errorf("Expected to not exist but got %v", i.value)
		}
	}
}

//Contains asserts the given array contains the informed value
func (i Info) Contains(theExpectedValue interface{}) func(t *testing.T) {
	return func(t *testing.T) {

		valueType := reflect.TypeOf(i.value).Kind()
		theExpectedValueType := reflect.TypeOf(theExpectedValue).Kind()

		value := reflect.ValueOf(i.value)

		if valueType != reflect.Slice {
			t.Errorf("The value infromed is of type %v", valueType)
		} else if value.Len() == 0 {
			t.Errorf("The value informed is an empty slice")
		} else {

			sliceType := reflect.TypeOf(value.Index(0).Interface()).Kind()

			if sliceType !=  theExpectedValueType {
				t.Errorf("The expected value informed is of type %v and the slice is of type %v", theExpectedValueType, sliceType)
			} else {

				found := false

				for j := 0; j < value.Len(); j++ {
					if value.Index(j).Interface() == theExpectedValue {
						found = true
						break
					}
				}

				if !found {
					t.Errorf("Informed value %v not found in slice %v", theExpectedValue,  i.value)
				}
			}
		}
	}
}<|endoftext|>


<commit_before>package html

import (
	"fmt"
	"github.com/vlifesystems/rulehuntersrv/config"
	"github.com/vlifesystems/rulehuntersrv/html/cmd"
	"github.com/vlifesystems/rulehuntersrv/internal/testhelpers"
	"github.com/vlifesystems/rulehuntersrv/progress"
	"github.com/vlifesystems/rulehuntersrv/quitter"
	"os"
	"path/filepath"
	"strconv"
	"testing"
	"time"
)

// This checks if Run will quit properly when told to
func TestRun_quit(t *testing.T) {
	q := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t)
	defer os.RemoveAll(cfgDir)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	go Run(config, pm, l, q, htmlCmds)
	time.Sleep(1 * time.Second)
	htmlCmds <- cmd.Flush
	go func() {
		const secsWait = 3.0
		<-time.After(secsWait * time.Second)
		q.Done()
		t.Fatalf("Run() didn't quit within %v seconds", secsWait)
	}()
	q.Quit()
}

func TestGenReportFilename(t *testing.T) {
	wwwDir := "/var/wwww"
	cases := []struct {
		stamp        time.Time
		title        string
		wantFilename string
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
			"This could be very interesting",
			filepath.Join(wwwDir, "reports", "2009", "11", "10",
				fmt.Sprintf("%s_this-could-be-very-interesting",
					genStampMagicString(
						time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
					),
				),
				"index.html")},
	}
	for _, c := range cases {
		got := genReportFilename(wwwDir, c.stamp, c.title)
		if got != c.wantFilename {
			t.Errorf("genReportFilename(%s, %s) got: %s, want: %s",
				c.stamp, c.title, got, c.wantFilename)
		}
	}
}

func TestGenReportURLDir(t *testing.T) {
	cases := []struct {
		stamp   time.Time
		title   string
		wantDir string
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
			"This could be very interesting",
			fmt.Sprintf("/reports/2009/11/10/%s_this-could-be-very-interesting/",
				genStampMagicString(
					time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
				),
			),
		},
	}
	for _, c := range cases {
		got := genReportURLDir(c.stamp, c.title)
		if got != c.wantDir {
			t.Errorf("genReportFilename(%s, %s) got: %s, want: %s",
				c.stamp, c.title, got, c.wantDir)
		}
	}
}

func TestMakeReportURLDir(t *testing.T) {
	tempDir := testhelpers.TempDir(t)
	defer os.RemoveAll(tempDir)
	cases := []struct {
		stamp         time.Time
		title         string
		wantDirExists string
		wantURLDir    string
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
			"This could be very interesting",
			filepath.Join(tempDir, "reports", "2009", "11", "10",
				fmt.Sprintf("%s_this-could-be-very-interesting/",
					genStampMagicString(
						time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
					),
				),
			),
			fmt.Sprintf("/reports/2009/11/10/%s_this-could-be-very-interesting/",
				genStampMagicString(
					time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
				),
			),
		},
	}
	for _, c := range cases {
		got, err := makeReportURLDir(tempDir, c.stamp, c.title)
		if err != nil {
			t.Errorf("makeReportURLDir(%s, %s, %s) err: %s",
				tempDir, c.stamp, c.title, err)
		}
		if got != c.wantURLDir {
			t.Errorf("makeReportURLDir(%s, %s, %s) got: %s, want: %s",
				tempDir, c.stamp, c.title, got, c.wantURLDir)
		}
		if !dirExists(c.wantDirExists) {
			t.Errorf("makeReportURLDir(%s, %s, %s)  - directory doesn't exist: %s",
				tempDir, c.stamp, c.title, c.wantDirExists)
		}
	}
}

func TestEscapeString(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{"This is a TITLE",
			"this-is-a-title"},
		{"  hello how are % you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"--  hello how are %^& you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"hello((_ how are % you423 33  today",
			"hello-how-are-you423-33-today"},
		{"", ""},
	}
	for _, c := range cases {
		got := escapeString(c.in)
		if got != c.want {
			t.Errorf("escapeString(%s) got: %s, want: %s", c.in, got, c.want)
		}
	}
}

func TestGenStampMagicString(t *testing.T) {
	cases := []struct {
		in       time.Time
		wantDiff uint64
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2009, time.November, 11, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2009, time.December, 11, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2010, time.December, 11, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2009, time.November, 10, 22, 19, 19, 17, time.UTC), 1},
		{time.Date(2009, time.November, 10, 22, 19, 29, 17, time.UTC), 11},
		{time.Date(2009, time.November, 10, 22, 20, 18, 17, time.UTC), 60},
		{time.Date(2009, time.November, 10, 23, 19, 18, 17, time.UTC), 3600},
	}

	initStamp := time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC)
	initMagicStr := genStampMagicString(initStamp)

	initMagicNum, err := strconv.ParseUint(initMagicStr, 36, 64)
	if err != nil {
		t.Errorf("ParseUint(%s, 36, 64) err: %s", initMagicStr, err)
		return
	}

	for _, c := range cases {
		magicStr := genStampMagicString(c.in)
		magicNum, err := strconv.ParseUint(magicStr, 36, 64)
		if err != nil {
			t.Errorf("ParseUint(%s, 36, 64) err: %s", magicStr, err)
			return
		}
		diff := magicNum - initMagicNum
		if diff != c.wantDiff {
			t.Errorf("diff != wantDiff for stamp: %s got: %d, want: %d",
				c.in, diff, c.wantDiff)
		}
	}
}

func dirExists(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	return fi.IsDir()
}
<commit_msg>Increase secsWait to 5 for TestRun_quit<commit_after>package html

import (
	"fmt"
	"github.com/vlifesystems/rulehuntersrv/config"
	"github.com/vlifesystems/rulehuntersrv/html/cmd"
	"github.com/vlifesystems/rulehuntersrv/internal/testhelpers"
	"github.com/vlifesystems/rulehuntersrv/progress"
	"github.com/vlifesystems/rulehuntersrv/quitter"
	"os"
	"path/filepath"
	"strconv"
	"testing"
	"time"
)

// This checks if Run will quit properly when told to
func TestRun_quit(t *testing.T) {
	q := quitter.New()
	l := testhelpers.NewLogger()
	htmlCmds := make(chan cmd.Cmd)
	wd, err := os.Getwd()
	if err != nil {
		t.Fatal("Getwd() err: ", err)
	}
	defer os.Chdir(wd)
	cfgDir := testhelpers.BuildConfigDirs(t)
	defer os.RemoveAll(cfgDir)

	pm, err := progress.NewMonitor(
		filepath.Join(cfgDir, "build", "progress"),
		htmlCmds,
	)
	if err != nil {
		t.Fatalf("NewMonitor() err: %v", err)
	}
	config := &config.Config{
		ExperimentsDir:    filepath.Join(cfgDir, "experiments"),
		WWWDir:            filepath.Join(cfgDir, "www"),
		BuildDir:          filepath.Join(cfgDir, "build"),
		MaxNumReportRules: 100,
	}
	go Run(config, pm, l, q, htmlCmds)
	time.Sleep(1 * time.Second)
	htmlCmds <- cmd.Flush
	go func() {
		const secsWait = 5.0
		<-time.After(secsWait * time.Second)
		q.Done()
		t.Fatalf("Run() didn't quit within %v seconds", secsWait)
	}()
	q.Quit()
}

func TestGenReportFilename(t *testing.T) {
	wwwDir := "/var/wwww"
	cases := []struct {
		stamp        time.Time
		title        string
		wantFilename string
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
			"This could be very interesting",
			filepath.Join(wwwDir, "reports", "2009", "11", "10",
				fmt.Sprintf("%s_this-could-be-very-interesting",
					genStampMagicString(
						time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
					),
				),
				"index.html")},
	}
	for _, c := range cases {
		got := genReportFilename(wwwDir, c.stamp, c.title)
		if got != c.wantFilename {
			t.Errorf("genReportFilename(%s, %s) got: %s, want: %s",
				c.stamp, c.title, got, c.wantFilename)
		}
	}
}

func TestGenReportURLDir(t *testing.T) {
	cases := []struct {
		stamp   time.Time
		title   string
		wantDir string
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
			"This could be very interesting",
			fmt.Sprintf("/reports/2009/11/10/%s_this-could-be-very-interesting/",
				genStampMagicString(
					time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
				),
			),
		},
	}
	for _, c := range cases {
		got := genReportURLDir(c.stamp, c.title)
		if got != c.wantDir {
			t.Errorf("genReportFilename(%s, %s) got: %s, want: %s",
				c.stamp, c.title, got, c.wantDir)
		}
	}
}

func TestMakeReportURLDir(t *testing.T) {
	tempDir := testhelpers.TempDir(t)
	defer os.RemoveAll(tempDir)
	cases := []struct {
		stamp         time.Time
		title         string
		wantDirExists string
		wantURLDir    string
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
			"This could be very interesting",
			filepath.Join(tempDir, "reports", "2009", "11", "10",
				fmt.Sprintf("%s_this-could-be-very-interesting/",
					genStampMagicString(
						time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
					),
				),
			),
			fmt.Sprintf("/reports/2009/11/10/%s_this-could-be-very-interesting/",
				genStampMagicString(
					time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC),
				),
			),
		},
	}
	for _, c := range cases {
		got, err := makeReportURLDir(tempDir, c.stamp, c.title)
		if err != nil {
			t.Errorf("makeReportURLDir(%s, %s, %s) err: %s",
				tempDir, c.stamp, c.title, err)
		}
		if got != c.wantURLDir {
			t.Errorf("makeReportURLDir(%s, %s, %s) got: %s, want: %s",
				tempDir, c.stamp, c.title, got, c.wantURLDir)
		}
		if !dirExists(c.wantDirExists) {
			t.Errorf("makeReportURLDir(%s, %s, %s)  - directory doesn't exist: %s",
				tempDir, c.stamp, c.title, c.wantDirExists)
		}
	}
}

func TestEscapeString(t *testing.T) {
	cases := []struct {
		in   string
		want string
	}{
		{"This is a TITLE",
			"this-is-a-title"},
		{"  hello how are % you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"--  hello how are %^& you423 33  today __ --",
			"hello-how-are-you423-33-today"},
		{"hello((_ how are % you423 33  today",
			"hello-how-are-you423-33-today"},
		{"", ""},
	}
	for _, c := range cases {
		got := escapeString(c.in)
		if got != c.want {
			t.Errorf("escapeString(%s) got: %s, want: %s", c.in, got, c.want)
		}
	}
}

func TestGenStampMagicString(t *testing.T) {
	cases := []struct {
		in       time.Time
		wantDiff uint64
	}{
		{time.Date(2009, time.November, 10, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2009, time.November, 11, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2009, time.December, 11, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2010, time.December, 11, 22, 19, 18, 200, time.UTC), 0},
		{time.Date(2009, time.November, 10, 22, 19, 19, 17, time.UTC), 1},
		{time.Date(2009, time.November, 10, 22, 19, 29, 17, time.UTC), 11},
		{time.Date(2009, time.November, 10, 22, 20, 18, 17, time.UTC), 60},
		{time.Date(2009, time.November, 10, 23, 19, 18, 17, time.UTC), 3600},
	}

	initStamp := time.Date(2009, time.November, 10, 22, 19, 18, 17, time.UTC)
	initMagicStr := genStampMagicString(initStamp)

	initMagicNum, err := strconv.ParseUint(initMagicStr, 36, 64)
	if err != nil {
		t.Errorf("ParseUint(%s, 36, 64) err: %s", initMagicStr, err)
		return
	}

	for _, c := range cases {
		magicStr := genStampMagicString(c.in)
		magicNum, err := strconv.ParseUint(magicStr, 36, 64)
		if err != nil {
			t.Errorf("ParseUint(%s, 36, 64) err: %s", magicStr, err)
			return
		}
		diff := magicNum - initMagicNum
		if diff != c.wantDiff {
			t.Errorf("diff != wantDiff for stamp: %s got: %d, want: %d",
				c.in, diff, c.wantDiff)
		}
	}
}

func dirExists(path string) bool {
	fi, err := os.Stat(path)
	if err != nil {
		return false
	}
	return fi.IsDir()
}
<|endoftext|>