<commit_before>// Copyright 2017 The casbin Authors. 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 defaultrolemanager

import (
	"fmt"
	"strings"
	"sync"

	"github.com/casbin/casbin/v2/errors"
	"github.com/casbin/casbin/v2/log"
	"github.com/casbin/casbin/v2/rbac"
	"github.com/casbin/casbin/v2/util"
)

const defaultDomain string = ""
const defaultSeparator = "::"

type MatchingFunc func(arg1, arg2 string) bool

// RoleManager provides a default implementation for the RoleManager interface
type RoleManager struct {
	roles              *Roles
	domains            map[string]struct{}
	maxHierarchyLevel  int
	hasPattern         bool
	matchingFunc       MatchingFunc
	hasDomainPattern   bool
	domainMatchingFunc MatchingFunc

	logger log.Logger
}

// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
	rm := RoleManager{}
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	rm.maxHierarchyLevel = maxHierarchyLevel
	rm.hasPattern = false
	rm.hasDomainPattern = false

	rm.SetLogger(&log.DefaultLogger{})

	return &rm
}

// AddMatchingFunc support use pattern in g
func (rm *RoleManager) AddMatchingFunc(name string, fn MatchingFunc) {
	rm.hasPattern = true
	rm.matchingFunc = fn
}

// AddDomainMatchingFunc support use domain pattern in g
func (rm *RoleManager) AddDomainMatchingFunc(name string, fn MatchingFunc) {
	rm.hasDomainPattern = true
	rm.domainMatchingFunc = fn
}

// SetLogger sets role manager's logger.
func (rm *RoleManager) SetLogger(logger log.Logger) {
	rm.logger = logger
}

// Clear clears all stored data and resets the role manager to the initial state.
func (rm *RoleManager) Clear() error {
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	return nil
}

// AddLink adds the inheritance link between role: name1 and role: name2.
// aka role: name1 inherits role: name2.
func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		rm.domains[domain[0]] = struct{}{}
		patternDomain := rm.getPatternDomain(domain[0])

		for _, domain := range patternDomain {
			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			role1 := rm.roles.createRole(name1WithDomain)
			role2 := rm.roles.createRole(name2WithDomain)
			role1.addRole(role2)

			if rm.hasPattern {
				rm.roles.Range(func(key, value interface{}) bool {
					domainPattern, namePattern := getNameAndDomain(key.(string))
					if domainPattern != domain {
						return true
					}
					if rm.matchingFunc(namePattern, name1) && name1 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(namePattern, name2) && name2 != namePattern {
						role2.addRole(value.(*Role))
					}
					if rm.matchingFunc(name1, namePattern) && name1 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(name2, namePattern) && name2 != namePattern {
						role2.addRole(value.(*Role))
					}
					return true
				})
			}
		}
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}

}

// DeleteLink deletes the inheritance link between role: name1 and role: name2.
// aka role: name1 does not inherit role: name2 any more.
func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		name1WithDomain := getNameWithDomain(domain[0], name1)
		name2WithDomain := getNameWithDomain(domain[0], name2)

		_, ok1 := rm.roles.Load(name1WithDomain)
		_, ok2 := rm.roles.Load(name2WithDomain)
		if !ok1 || !ok2 {
			return errors.ERR_NAMES12_NOT_FOUND
		}

		role1 := rm.roles.createRole(name1WithDomain)
		role2 := rm.roles.createRole(name2WithDomain)
		role1.deleteRole(role2)
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}
}

func (rm *RoleManager) getPatternDomain(domain string) []string {
	matchedDomains := []string{domain}
	if rm.hasDomainPattern {
		for domainPattern := range rm.domains {
			if domain != domainPattern && rm.domainMatchingFunc(domain, domainPattern) {
				matchedDomains = append(matchedDomains, domainPattern)
			}
		}
	}
	return matchedDomains
}

// HasLink determines whether role: name1 inherits role: name2.
func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		if name1 == name2 {
			return true, nil
		}

		matchedDomain := rm.getPatternDomain(domain[0])

		for _, domain := range matchedDomain {
			if !rm.roles.hasRole(domain, name1, rm.matchingFunc) || !rm.roles.hasRole(domain, name2, rm.matchingFunc) {
				continue
			}

			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			if rm.hasPattern {
				flag := false
				rm.roles.Range(func(key, value interface{}) bool {
					nameWithDomain := key.(string)
					_, name := getNameAndDomain(nameWithDomain)
					if rm.matchingFunc(name1, name) && value.(*Role).hasRoleWithMatchingFunc(domain, name2, rm.maxHierarchyLevel, rm.matchingFunc) {
						flag = true
						return false
					}
					return true
				})
				if flag {
					return true, nil
				}
			} else {
				role1 := rm.roles.createRole(name1WithDomain)
				result := role1.hasRole(name2WithDomain, rm.maxHierarchyLevel)
				if result {
					return true, nil
				}
			}
		}
		return false, nil
	default:
		return false, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetRoles gets the roles that a subject inherits.
func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var gottenRoles []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.matchingFunc) {
				continue
			}
			gottenRoles = append(gottenRoles, rm.roles.createRole(nameWithDomain).getRoles()...)
		}
		gottenRoles = util.RemoveDuplicateElement(gottenRoles)
		return gottenRoles, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetUsers gets the users that inherits a subject.
// domain is an unreferenced parameter here, may be used in other implementations.
func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var names []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.domainMatchingFunc) {
				return nil, errors.ERR_NAME_NOT_FOUND
			}

			rm.roles.Range(func(_, value interface{}) bool {
				role := value.(*Role)
				if role.hasDirectRole(nameWithDomain) {
					_, roleName := getNameAndDomain(role.nameWithDomain)
					names = append(names, roleName)
				}
				return true
			})
		}

		return names, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// PrintRoles prints all the roles to log.
func (rm *RoleManager) PrintRoles() error {
	if !(rm.logger).IsEnabled() {
		return nil
	}

	var roles []string
	rm.roles.Range(func(_, value interface{}) bool {
		if text := value.(*Role).toString(); text != "" {
			roles = append(roles, text)
		}
		return true
	})

	rm.logger.LogRole(roles)
	return nil
}

// Roles represents all roles in a domain
type Roles struct {
	sync.Map
}

func (roles *Roles) hasRole(domain, name string, matchingFunc MatchingFunc) bool {
	var ok bool
	if matchingFunc != nil {
		roles.Range(func(key, value interface{}) bool {
			domainPattern, namePattern := getNameAndDomain(key.(string))
			if domainPattern == domain && matchingFunc(name, namePattern) {
				ok = true
			}
			return true
		})
	} else {
		_, ok = roles.Load(getNameWithDomain(domain, name))
	}

	return ok
}

func (roles *Roles) createRole(name string) *Role {
	role, _ := roles.LoadOrStore(name, newRole(name))
	return role.(*Role)
}

// Role represents the data structure for a role in RBAC.
type Role struct {
	nameWithDomain string
	roles          []*Role
}

func newRole(name string) *Role {
	r := Role{}
	r.nameWithDomain = name
	return &r
}

func (r *Role) addRole(role *Role) {
	// determine whether this role has been added
	for _, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			return
		}
	}

	r.roles = append(r.roles, role)
}

func (r *Role) deleteRole(role *Role) {
	for i, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			r.roles = append(r.roles[:i], r.roles[i+1:]...)
			return
		}
	}
}

func (r *Role) hasRole(nameWithDomain string, hierarchyLevel int) bool {
	if r.hasDirectRole(nameWithDomain) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRole(nameWithDomain, hierarchyLevel-1) {
			return true
		}
	}
	return false
}

func (r *Role) hasRoleWithMatchingFunc(domain, name string, hierarchyLevel int, matchingFunc MatchingFunc) bool {
	if r.hasDirectRoleWithMatchingFunc(domain, name, matchingFunc) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRoleWithMatchingFunc(domain, name, hierarchyLevel-1, matchingFunc) {
			return true
		}
	}
	return false
}

func (r *Role) hasDirectRole(nameWithDomain string) bool {
	for _, role := range r.roles {
		if role.nameWithDomain == nameWithDomain {
			return true
		}
	}

	return false
}

func (r *Role) hasDirectRoleWithMatchingFunc(domain, name string, matchingFunc MatchingFunc) bool {
	roleWithDomain := getNameWithDomain(domain, name)
	for _, role := range r.roles {
		roleDomain, roleName := getNameAndDomain(role.nameWithDomain)
		if role.nameWithDomain == roleWithDomain || (matchingFunc(name, roleName) && roleDomain == domain) {
			return true
		}
	}

	return false
}

func (r *Role) toString() string {
	if len(r.roles) == 0 {
		return ""
	}

	var sb strings.Builder
	sb.WriteString(r.nameWithDomain)
	sb.WriteString(" < ")
	if len(r.roles) != 1 {
		sb.WriteString("(")
	}

	for i, role := range r.roles {
		if i == 0 {
			sb.WriteString(role.nameWithDomain)
		} else {
			sb.WriteString(", ")
			sb.WriteString(role.nameWithDomain)
		}
	}

	if len(r.roles) != 1 {
		sb.WriteString(")")
	}

	return sb.String()
}

func (r *Role) getRoles() []string {
	names := []string{}
	for _, role := range r.roles {
		_, roleName := getNameAndDomain(role.nameWithDomain)
		names = append(names, roleName)
	}
	return names
}

func getNameWithDomain(domain, name string) string {
	if domain == "" {
		return name
	}
	return fmt.Sprintf("%s%s%s", domain, defaultSeparator, name)
}

func getNameAndDomain(domainAndName string) (string, string) {
	t := strings.Split(domainAndName, defaultSeparator)
	if len(t) == 1 {
		return defaultDomain, t[0]
	}
	return t[0], t[1]
}
<commit_msg>fix: HasLink wrong<commit_after>// Copyright 2017 The casbin Authors. 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 defaultrolemanager

import (
	"fmt"
	"strings"
	"sync"

	"github.com/casbin/casbin/v2/errors"
	"github.com/casbin/casbin/v2/log"
	"github.com/casbin/casbin/v2/rbac"
	"github.com/casbin/casbin/v2/util"
)

const defaultDomain string = ""
const defaultSeparator = "::"

type MatchingFunc func(arg1, arg2 string) bool

// RoleManager provides a default implementation for the RoleManager interface
type RoleManager struct {
	roles              *Roles
	domains            map[string]struct{}
	maxHierarchyLevel  int
	hasPattern         bool
	matchingFunc       MatchingFunc
	hasDomainPattern   bool
	domainMatchingFunc MatchingFunc

	logger log.Logger
}

// NewRoleManager is the constructor for creating an instance of the
// default RoleManager implementation.
func NewRoleManager(maxHierarchyLevel int) rbac.RoleManager {
	rm := RoleManager{}
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	rm.maxHierarchyLevel = maxHierarchyLevel
	rm.hasPattern = false
	rm.hasDomainPattern = false

	rm.SetLogger(&log.DefaultLogger{})

	return &rm
}

// AddMatchingFunc support use pattern in g
func (rm *RoleManager) AddMatchingFunc(name string, fn MatchingFunc) {
	rm.hasPattern = true
	rm.matchingFunc = fn
}

// AddDomainMatchingFunc support use domain pattern in g
func (rm *RoleManager) AddDomainMatchingFunc(name string, fn MatchingFunc) {
	rm.hasDomainPattern = true
	rm.domainMatchingFunc = fn
}

// SetLogger sets role manager's logger.
func (rm *RoleManager) SetLogger(logger log.Logger) {
	rm.logger = logger
}

// Clear clears all stored data and resets the role manager to the initial state.
func (rm *RoleManager) Clear() error {
	rm.roles = &Roles{sync.Map{}}
	rm.domains = make(map[string]struct{})
	return nil
}

// AddLink adds the inheritance link between role: name1 and role: name2.
// aka role: name1 inherits role: name2.
func (rm *RoleManager) AddLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		rm.domains[domain[0]] = struct{}{}
		patternDomain := rm.getPatternDomain(domain[0])

		for _, domain := range patternDomain {
			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			role1 := rm.roles.createRole(name1WithDomain)
			role2 := rm.roles.createRole(name2WithDomain)
			role1.addRole(role2)

			if rm.hasPattern {
				rm.roles.Range(func(key, value interface{}) bool {
					domainPattern, namePattern := getNameAndDomain(key.(string))
					if domainPattern != domain {
						return true
					}
					if rm.matchingFunc(namePattern, name1) && name1 != namePattern && name2 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(namePattern, name2) && name2 != namePattern && name1 != namePattern {
						role2.addRole(value.(*Role))
					}
					if rm.matchingFunc(name1, namePattern) && name1 != namePattern && name2 != namePattern {
						valueRole, _ := rm.roles.LoadOrStore(key.(string), newRole(key.(string)))
						valueRole.(*Role).addRole(role1)
					}
					if rm.matchingFunc(name2, namePattern) && name2 != namePattern && name1 != namePattern {
						role2.addRole(value.(*Role))
					}
					return true
				})
			}
		}
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}

}

// DeleteLink deletes the inheritance link between role: name1 and role: name2.
// aka role: name1 does not inherit role: name2 any more.
func (rm *RoleManager) DeleteLink(name1 string, name2 string, domain ...string) error {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		name1WithDomain := getNameWithDomain(domain[0], name1)
		name2WithDomain := getNameWithDomain(domain[0], name2)

		_, ok1 := rm.roles.Load(name1WithDomain)
		_, ok2 := rm.roles.Load(name2WithDomain)
		if !ok1 || !ok2 {
			return errors.ERR_NAMES12_NOT_FOUND
		}

		role1 := rm.roles.createRole(name1WithDomain)
		role2 := rm.roles.createRole(name2WithDomain)
		role1.deleteRole(role2)
		return nil
	default:
		return errors.ERR_DOMAIN_PARAMETER
	}
}

func (rm *RoleManager) getPatternDomain(domain string) []string {
	matchedDomains := []string{domain}
	if rm.hasDomainPattern {
		for domainPattern := range rm.domains {
			if domain != domainPattern && rm.domainMatchingFunc(domain, domainPattern) {
				matchedDomains = append(matchedDomains, domainPattern)
			}
		}
	}
	return matchedDomains
}

// HasLink determines whether role: name1 inherits role: name2.
func (rm *RoleManager) HasLink(name1 string, name2 string, domain ...string) (bool, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		if name1 == name2 {
			return true, nil
		}

		matchedDomain := rm.getPatternDomain(domain[0])

		for _, domain := range matchedDomain {
			if !rm.roles.hasRole(domain, name1, rm.matchingFunc) || !rm.roles.hasRole(domain, name2, rm.matchingFunc) {
				continue
			}

			name1WithDomain := getNameWithDomain(domain, name1)
			name2WithDomain := getNameWithDomain(domain, name2)

			if rm.hasPattern {
				flag := false
				rm.roles.Range(func(key, value interface{}) bool {
					nameWithDomain := key.(string)
					keyDomain, name := getNameAndDomain(nameWithDomain)
					if rm.hasDomainPattern {
						if !rm.domainMatchingFunc(domain, keyDomain) {
							return true
						}
					} else if domain != keyDomain {
						return true
					}
					if rm.matchingFunc(name1, name) && value.(*Role).hasRoleWithMatchingFunc(domain, name2, rm.maxHierarchyLevel, rm.matchingFunc) {
						flag = true
						return false
					}
					return true
				})
				if flag {
					return true, nil
				}
			} else {
				role1 := rm.roles.createRole(name1WithDomain)
				result := role1.hasRole(name2WithDomain, rm.maxHierarchyLevel)
				if result {
					return true, nil
				}
			}
		}
		return false, nil
	default:
		return false, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetRoles gets the roles that a subject inherits.
func (rm *RoleManager) GetRoles(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var gottenRoles []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.matchingFunc) {
				continue
			}
			gottenRoles = append(gottenRoles, rm.roles.createRole(nameWithDomain).getRoles()...)
		}
		gottenRoles = util.RemoveDuplicateElement(gottenRoles)
		return gottenRoles, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// GetUsers gets the users that inherits a subject.
// domain is an unreferenced parameter here, may be used in other implementations.
func (rm *RoleManager) GetUsers(name string, domain ...string) ([]string, error) {
	switch len(domain) {
	case 0:
		domain = []string{defaultDomain}
		fallthrough
	case 1:
		patternDomain := rm.getPatternDomain(domain[0])

		var names []string
		for _, domain := range patternDomain {
			nameWithDomain := getNameWithDomain(domain, name)
			if !rm.roles.hasRole(domain, name, rm.domainMatchingFunc) {
				return nil, errors.ERR_NAME_NOT_FOUND
			}

			rm.roles.Range(func(_, value interface{}) bool {
				role := value.(*Role)
				if role.hasDirectRole(nameWithDomain) {
					_, roleName := getNameAndDomain(role.nameWithDomain)
					names = append(names, roleName)
				}
				return true
			})
		}

		return names, nil
	default:
		return nil, errors.ERR_DOMAIN_PARAMETER
	}
}

// PrintRoles prints all the roles to log.
func (rm *RoleManager) PrintRoles() error {
	if !(rm.logger).IsEnabled() {
		return nil
	}

	var roles []string
	rm.roles.Range(func(_, value interface{}) bool {
		if text := value.(*Role).toString(); text != "" {
			roles = append(roles, text)
		}
		return true
	})

	rm.logger.LogRole(roles)
	return nil
}

// Roles represents all roles in a domain
type Roles struct {
	sync.Map
}

func (roles *Roles) hasRole(domain, name string, matchingFunc MatchingFunc) bool {
	var ok bool
	if matchingFunc != nil {
		roles.Range(func(key, value interface{}) bool {
			domainPattern, namePattern := getNameAndDomain(key.(string))
			if domainPattern == domain && matchingFunc(name, namePattern) {
				ok = true
			}
			return true
		})
	} else {
		_, ok = roles.Load(getNameWithDomain(domain, name))
	}

	return ok
}

func (roles *Roles) createRole(name string) *Role {
	role, _ := roles.LoadOrStore(name, newRole(name))
	return role.(*Role)
}

// Role represents the data structure for a role in RBAC.
type Role struct {
	nameWithDomain string
	roles          []*Role
}

func newRole(name string) *Role {
	r := Role{}
	r.nameWithDomain = name
	return &r
}

func (r *Role) addRole(role *Role) {
	// determine whether this role has been added
	for _, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			return
		}
	}

	r.roles = append(r.roles, role)
}

func (r *Role) deleteRole(role *Role) {
	for i, rr := range r.roles {
		if rr.nameWithDomain == role.nameWithDomain {
			r.roles = append(r.roles[:i], r.roles[i+1:]...)
			return
		}
	}
}

func (r *Role) hasRole(nameWithDomain string, hierarchyLevel int) bool {
	if r.hasDirectRole(nameWithDomain) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRole(nameWithDomain, hierarchyLevel-1) {
			return true
		}
	}
	return false
}

func (r *Role) hasRoleWithMatchingFunc(domain, name string, hierarchyLevel int, matchingFunc MatchingFunc) bool {
	if r.hasDirectRoleWithMatchingFunc(domain, name, matchingFunc) {
		return true
	}

	if hierarchyLevel <= 0 {
		return false
	}

	for _, role := range r.roles {
		if role.hasRoleWithMatchingFunc(domain, name, hierarchyLevel-1, matchingFunc) {
			return true
		}
	}
	return false
}

func (r *Role) hasDirectRole(nameWithDomain string) bool {
	for _, role := range r.roles {
		if role.nameWithDomain == nameWithDomain {
			return true
		}
	}

	return false
}

func (r *Role) hasDirectRoleWithMatchingFunc(domain, name string, matchingFunc MatchingFunc) bool {
	roleWithDomain := getNameWithDomain(domain, name)
	for _, role := range r.roles {
		roleDomain, roleName := getNameAndDomain(role.nameWithDomain)
		if role.nameWithDomain == roleWithDomain || (matchingFunc(name, roleName) && roleDomain == domain && name != roleName) {
			return true
		}
	}

	return false
}

func (r *Role) toString() string {
	if len(r.roles) == 0 {
		return ""
	}

	var sb strings.Builder
	sb.WriteString(r.nameWithDomain)
	sb.WriteString(" < ")
	if len(r.roles) != 1 {
		sb.WriteString("(")
	}

	for i, role := range r.roles {
		if i == 0 {
			sb.WriteString(role.nameWithDomain)
		} else {
			sb.WriteString(", ")
			sb.WriteString(role.nameWithDomain)
		}
	}

	if len(r.roles) != 1 {
		sb.WriteString(")")
	}

	return sb.String()
}

func (r *Role) getRoles() []string {
	names := []string{}
	for _, role := range r.roles {
		_, roleName := getNameAndDomain(role.nameWithDomain)
		names = append(names, roleName)
	}
	return names
}

func getNameWithDomain(domain, name string) string {
	if domain == "" {
		return name
	}
	return fmt.Sprintf("%s%s%s", domain, defaultSeparator, name)
}

func getNameAndDomain(domainAndName string) (string, string) {
	t := strings.Split(domainAndName, defaultSeparator)
	if len(t) == 1 {
		return defaultDomain, t[0]
	}
	return t[0], t[1]
}
<|endoftext|>


<commit_before>package cmd

import (
	"runtime"
	"testing"

	"github.com/exercism/cli/config"
	"github.com/stretchr/testify/assert"
)

type testCase struct {
	desc           string
	args           []string
	existingUsrCfg *config.UserConfig
	expectedUsrCfg *config.UserConfig
	existingAPICfg *config.APIConfig
	expectedAPICfg *config.APIConfig
}

func TestConfigure(t *testing.T) {
	testCases := []testCase{
		testCase{
			desc:           "It writes the flags when there is no config file.",
			args:           []string{"fakeapp", "configure", "--token", "a", "--workspace", "/a", "--api", "http://example.com"},
			existingUsrCfg: nil,
			expectedUsrCfg: &config.UserConfig{Token: "a", Workspace: "/a"},
			existingAPICfg: nil,
			expectedAPICfg: &config.APIConfig{BaseURL: "http://example.com"},
		},
		testCase{
			desc:           "It overwrites the flags in the config file.",
			args:           []string{"fakeapp", "configure", "--token", "b", "--workspace", "/b", "--api", "http://example.com/v2"},
			existingUsrCfg: &config.UserConfig{Token: "token-b", Workspace: "/workspace-b"},
			expectedUsrCfg: &config.UserConfig{Token: "b", Workspace: "/b"},
			existingAPICfg: &config.APIConfig{BaseURL: "http://example.com/v1"},
			expectedAPICfg: &config.APIConfig{BaseURL: "http://example.com/v2"},
		},
		testCase{
			desc:           "It overwrites the flags that are passed, without losing the ones that are not.",
			args:           []string{"fakeapp", "configure", "--token", "c"},
			existingUsrCfg: &config.UserConfig{Token: "token-c", Workspace: "/workspace-c"},
			expectedUsrCfg: &config.UserConfig{Token: "c", Workspace: "/workspace-c"},
		},
		testCase{
			desc:           "It gets the default API base URL.",
			args:           []string{"fakeapp", "configure"},
			existingAPICfg: &config.APIConfig{},
			expectedAPICfg: &config.APIConfig{BaseURL: "https://v2.exercism.io/api/v1"},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, makeTest(tc))
	}
}

func makeTest(tc testCase) func(*testing.T) {

	return func(t *testing.T) {
		cmdTest := &CommandTest{
			Cmd:    configureCmd,
			InitFn: initConfigureCmd,
			Args:   tc.args,
		}
		cmdTest.Setup(t)
		defer cmdTest.Teardown(t)

		if tc.existingUsrCfg != nil {
			// Write a fake config.
			cfg := config.NewEmptyUserConfig()
			cfg.Token = tc.existingUsrCfg.Token
			cfg.Workspace = tc.existingUsrCfg.Workspace
			err := cfg.Write()
			assert.NoError(t, err, tc.desc)
		}

		cmdTest.App.Execute()

		if tc.expectedUsrCfg != nil {
			if runtime.GOOS == "windows" {
				tc.expectedUsrCfg.Normalize()
			}

			usrCfg, err := config.NewUserConfig()

			assert.NoError(t, err, tc.desc)
			assert.Equal(t, tc.expectedUsrCfg.Token, usrCfg.Token, tc.desc)
			assert.Equal(t, tc.expectedUsrCfg.Workspace, usrCfg.Workspace, tc.desc)
		}

		if tc.expectedAPICfg != nil {
			apiCfg, err := config.NewAPIConfig()
			assert.NoError(t, err, tc.desc)
			assert.Equal(t, tc.expectedAPICfg.BaseURL, apiCfg.BaseURL, tc.desc)
		}
	}
}
<commit_msg>Delete config files between tests in TestConfigure.<commit_after>package cmd

import (
	"os"
	"runtime"
	"testing"

	"github.com/exercism/cli/config"
	"github.com/stretchr/testify/assert"
)

type testCase struct {
	desc           string
	args           []string
	existingUsrCfg *config.UserConfig
	expectedUsrCfg *config.UserConfig
	existingAPICfg *config.APIConfig
	expectedAPICfg *config.APIConfig
}

func TestConfigure(t *testing.T) {
	testCases := []testCase{
		testCase{
			desc:           "It writes the flags when there is no config file.",
			args:           []string{"fakeapp", "configure", "--token", "a", "--workspace", "/a", "--api", "http://example.com"},
			existingUsrCfg: nil,
			expectedUsrCfg: &config.UserConfig{Token: "a", Workspace: "/a"},
			existingAPICfg: nil,
			expectedAPICfg: &config.APIConfig{BaseURL: "http://example.com"},
		},
		testCase{
			desc:           "It overwrites the flags in the config file.",
			args:           []string{"fakeapp", "configure", "--token", "b", "--workspace", "/b", "--api", "http://example.com/v2"},
			existingUsrCfg: &config.UserConfig{Token: "token-b", Workspace: "/workspace-b"},
			expectedUsrCfg: &config.UserConfig{Token: "b", Workspace: "/b"},
			existingAPICfg: &config.APIConfig{BaseURL: "http://example.com/v1"},
			expectedAPICfg: &config.APIConfig{BaseURL: "http://example.com/v2"},
		},
		testCase{
			desc:           "It overwrites the flags that are passed, without losing the ones that are not.",
			args:           []string{"fakeapp", "configure", "--token", "c"},
			existingUsrCfg: &config.UserConfig{Token: "token-c", Workspace: "/workspace-c"},
			expectedUsrCfg: &config.UserConfig{Token: "c", Workspace: "/workspace-c"},
		},
		testCase{
			desc:           "It gets the default API base URL.",
			args:           []string{"fakeapp", "configure"},
			existingAPICfg: &config.APIConfig{},
			expectedAPICfg: &config.APIConfig{BaseURL: "https://v2.exercism.io/api/v1"},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.desc, makeTest(tc))
	}
}

func makeTest(tc testCase) func(*testing.T) {

	return func(t *testing.T) {
		cmdTest := &CommandTest{
			Cmd:    configureCmd,
			InitFn: initConfigureCmd,
			Args:   tc.args,
		}
		cmdTest.Setup(t)
		defer cmdTest.Teardown(t)

		if tc.existingUsrCfg != nil {
			// Write a fake config.
			cfg := config.NewEmptyUserConfig()
			cfg.Token = tc.existingUsrCfg.Token
			cfg.Workspace = tc.existingUsrCfg.Workspace
			err := cfg.Write()
			assert.NoError(t, err, tc.desc)
		}

		cmdTest.App.Execute()

		if tc.expectedUsrCfg != nil {
			if runtime.GOOS == "windows" {
				tc.expectedUsrCfg.Normalize()
			}

			usrCfg, err := config.NewUserConfig()

			assert.NoError(t, err, tc.desc)
			assert.Equal(t, tc.expectedUsrCfg.Token, usrCfg.Token, tc.desc)
			assert.Equal(t, tc.expectedUsrCfg.Workspace, usrCfg.Workspace, tc.desc)
		}

		if tc.expectedAPICfg != nil {
			apiCfg, err := config.NewAPIConfig()
			assert.NoError(t, err, tc.desc)
			assert.Equal(t, tc.expectedAPICfg.BaseURL, apiCfg.BaseURL, tc.desc)
			os.Remove(apiCfg.File())
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"os"
	"fmt"
	"bufio"
	"github.com/janosgyerik/dupfinder"
)

// TODO
// take list of files from stdin
// print out duplicates visually grouped

func exit() {
	flag.Usage()
	os.Exit(1)
}

type Params struct {
	paths []string
}

func parseArgs() Params {
	flag.Usage = func() {
		fmt.Printf("Usage: %s\n\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()

	paths := make([]string, 0)
	if len(flag.Args()) > 0 {
		for _, arg := range flag.Args() {
			if isFile(arg) {
				paths = append(paths, arg)
			}
		}
	} else {
		paths = readFilePathsFromStdin()
	}

	if len(paths) == 0 {
		exit()
	}

	return Params{
		paths: paths,
	}
}

func readFilePathsFromStdin() []string {
	paths := make([]string, 0)

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if path := scanner.Text(); isFile(path) {
			paths = append(paths, path)
		}
	}

	return paths
}

func isFile(s string) bool {
	if _, err := os.Stat(s); err == nil {
		return true
	}
	return false
}

func main() {
	params := parseArgs()

	for _, dups := range dupfinder.FindDuplicates(params.paths...) {
		for _, path := range dups.GetPaths() {
			fmt.Println(path)
		}
		fmt.Println()
	}
}
<commit_msg>Fix isFile to return false for directories<commit_after>package main

import (
	"flag"
	"os"
	"fmt"
	"bufio"
	"github.com/janosgyerik/dupfinder"
)

// TODO
// take list of files from stdin
// print out duplicates visually grouped

func exit() {
	flag.Usage()
	os.Exit(1)
}

type Params struct {
	paths []string
}

func parseArgs() Params {
	flag.Usage = func() {
		fmt.Printf("Usage: %s\n\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.Parse()

	paths := make([]string, 0)
	if len(flag.Args()) > 0 {
		for _, arg := range flag.Args() {
			if isFile(arg) {
				paths = append(paths, arg)
			}
		}
	} else {
		paths = readFilePathsFromStdin()
	}

	if len(paths) == 0 {
		exit()
	}

	return Params{
		paths: paths,
	}
}

func readFilePathsFromStdin() []string {
	paths := make([]string, 0)

	scanner := bufio.NewScanner(os.Stdin)
	for scanner.Scan() {
		if path := scanner.Text(); isFile(path) {
			paths = append(paths, path)
		}
	}

	return paths
}

func isFile(s string) bool {
	if stat, err := os.Stat(s); err == nil && !stat.IsDir() {
		return true
	}
	return false
}

func main() {
	params := parseArgs()

	for _, dups := range dupfinder.FindDuplicates(params.paths...) {
		for _, path := range dups.GetPaths() {
			fmt.Println(path)
		}
		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/zlib"
	"encoding/ascii85"
	"flag"
	"fmt"
	"github.com/bnagy/pdflex"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
)

var xref = []byte("xref")
var startxref = []byte("startxref")
var trailer = []byte("trailer")
var pref85 = "<~"
var suff85 = "~>"

var (
	flagStrict = flag.Bool("strict", false, "Abort on xref parsing errors etc")
	flagMax    = flag.Int("max", 128, "Trim streams whose size is greater than this value")
)

func inflate(s string) (string, error) {
	in := strings.NewReader(s)
	decom, err := zlib.NewReader(in)
	if err != nil {
		return "", err
	}

	var b bytes.Buffer
	_, err = io.Copy(&b, decom)
	decom.Close()

	return b.String(), err
}

func deflate(s string) (string, error) {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	_, err := w.Write([]byte(s))
	w.Close()
	if err != nil {
		return "", err
	}

	return b.String(), nil
}

func un85(s string) (string, error) {
	// Caller is expected to trim <~ ~> if present
	s = strings.TrimPrefix(s, pref85)
	s = strings.TrimSuffix(s, suff85)
	dec := ascii85.NewDecoder(strings.NewReader(s))
	out, err := ioutil.ReadAll(dec)

	if err != nil {
		return "", err
	}

	return string(out), nil
}

func re85(s string) (string, error) {
	var b bytes.Buffer
	w := ascii85.NewEncoder(&b)
	_, err := w.Write([]byte(s))
	w.Close()
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

func shrink(in []byte, max int) ([]byte, error) {
	l := pdflex.NewLexer("", string(in))
	var out bytes.Buffer
	zipped := false
	asc85 := false
	var err error

	for i := l.NextItem(); i.Typ != pdflex.ItemEOF; i = l.NextItem() {
		if i.Typ == pdflex.ItemStreamBody {

			s := i.Val

			if asc85 {
				s, err = un85(s)
				if err != nil && *flagStrict {
					log.Fatalf("[STRICT] Failed to un85: %s", err)
				}
			}

			if zipped {
				s2, err := inflate(s)
				if err != nil && *flagStrict {
					log.Fatalf("[STRICT] Error unzipping internal stream: %s\n", err)
				}
				// If not strict, we ignore any errors here. If it's
				// unexpected EOF we'll get partial unzipped data, so use
				// that for truncation. Other errors will read a zero
				// length string, in which case we fall back to truncating
				// the original (corrupt) zipped stream.
				if len(s2) > 0 {
					s = s2
				}
			}

			if len(s) > max {
				s = s[:max]
			} else {
				// write the original string
				out.WriteString(i.Val)
				zipped = false
				asc85 = false
				continue
			}

			if zipped {
				s, err = deflate(s)
				if err != nil {
					// should never happen, strict mode or not
					return nil, fmt.Errorf("error zipping truncated string: %s", err)
				}
			}
			if asc85 {
				s, err = re85(s)
				if err != nil {
					// ditto
					return nil, fmt.Errorf("error Ascii85ing string: %s", err)
				}
			}

			out.WriteString(s)
			zipped = false
			asc85 = false

		} else {

			if i.Typ == pdflex.ItemName && i.Val == "/FlateDecode" {
				zipped = true
			}
			if i.Typ == pdflex.ItemName && i.Val == "/ASCII85Decode" {
				asc85 = true
			}
			out.WriteString(i.Val)
		}

		if i.Typ == pdflex.ItemError {
			break
		}
	}
	return out.Bytes(), nil
}

func fix(in []byte) []byte {
	p := pdflex.Parser{Lexer: pdflex.NewLexer("", string(in))}
	return p.FixXrefs()
}

func main() {

	flag.Usage = func() {
		fmt.Fprintf(
			os.Stderr,
			"  Usage: %s file [file file ...]\n"+
				"    -max=128: Trim streams whose size is greater than this value\n"+
				"    -strict=false: Abort on xref parsing errors etc\n",
			path.Base(os.Args[0]),
		)
	}

	flag.Parse()
	if *flagMax < 0 {
		flag.Usage()
		os.Exit(1)
	}

	for _, arg := range flag.Args() {

		fmt.Fprintf(os.Stderr, "[SHRINKING] %s\n", arg)

		// Read in
		raw, err := ioutil.ReadFile(arg)
		if err != nil {
			fmt.Fprintf(os.Stderr, "[SKIPPED] %s - %s\n", arg, err)
			continue
		}

		// Shrink
		shrunk, err := shrink(raw, *flagMax)
		if err != nil {
			fmt.Fprintf(os.Stderr, "[SKIPPED] %s - strict mode: %s\n", arg, err)
			continue
		}

		// Fix up xrefs
		fixed := fix(shrunk)

		// Write out
		newfn := strings.TrimSuffix(path.Base(arg), path.Ext(arg)) + "-small" + path.Ext(arg)
		newfn = path.Join(path.Dir(arg), newfn)
		err = ioutil.WriteFile(newfn, fixed, 0600)
		if err != nil {
			fmt.Fprintf(os.Stderr, "[SKIPPED] %s - %s", newfn, err)
		}
	}

}
<commit_msg>add concurrency option<commit_after>package main

import (
	"bytes"
	"compress/zlib"
	"encoding/ascii85"
	"flag"
	"fmt"
	"github.com/bnagy/pdflex"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
)

var xref = []byte("xref")
var startxref = []byte("startxref")
var trailer = []byte("trailer")
var pref85 = "<~"
var suff85 = "~>"

var (
	flagStrict  = flag.Bool("strict", false, "Abort on xref parsing errors etc")
	flagMax     = flag.Int("max", 128, "Trim streams whose size is greater than this value")
	flagWorkers = flag.Int("workers", 1, "Number of concurrent workers to use")
)

func inflate(s string) (string, error) {
	in := strings.NewReader(s)
	decom, err := zlib.NewReader(in)
	if err != nil {
		return "", err
	}

	var b bytes.Buffer
	_, err = io.Copy(&b, decom)
	decom.Close()

	return b.String(), err
}

func deflate(s string) (string, error) {
	var b bytes.Buffer
	w := zlib.NewWriter(&b)
	_, err := w.Write([]byte(s))
	w.Close()
	if err != nil {
		return "", err
	}

	return b.String(), nil
}

func un85(s string) (string, error) {
	// Caller is expected to trim <~ ~> if present
	s = strings.TrimPrefix(s, pref85)
	s = strings.TrimSuffix(s, suff85)
	dec := ascii85.NewDecoder(strings.NewReader(s))
	out, err := ioutil.ReadAll(dec)

	if err != nil {
		return "", err
	}

	return string(out), nil
}

func re85(s string) (string, error) {
	var b bytes.Buffer
	w := ascii85.NewEncoder(&b)
	_, err := w.Write([]byte(s))
	w.Close()
	if err != nil {
		return "", err
	}
	return b.String(), nil
}

func shrink(in []byte, max int) ([]byte, error) {
	l := pdflex.NewLexer("", string(in))
	var out bytes.Buffer
	zipped := false
	asc85 := false
	var err error

	for i := l.NextItem(); i.Typ != pdflex.ItemEOF; i = l.NextItem() {
		if i.Typ == pdflex.ItemStreamBody {

			s := i.Val

			if asc85 {
				s, err = un85(s)
				if err != nil && *flagStrict {
					log.Fatalf("[STRICT] Failed to un85: %s", err)
				}
			}

			if zipped {
				s2, err := inflate(s)
				if err != nil && *flagStrict {
					log.Fatalf("[STRICT] Error unzipping internal stream: %s\n", err)
				}
				// If not strict, we ignore any errors here. If it's
				// unexpected EOF we'll get partial unzipped data, so use
				// that for truncation. Other errors will read a zero
				// length string, in which case we fall back to truncating
				// the original (corrupt) zipped stream.
				if len(s2) > 0 {
					s = s2
				}
			}

			if len(s) > max {
				s = s[:max]
			} else {
				// write the original string
				out.WriteString(i.Val)
				zipped = false
				asc85 = false
				continue
			}

			if zipped {
				s, err = deflate(s)
				if err != nil {
					// should never happen, strict mode or not
					return nil, fmt.Errorf("error zipping truncated string: %s", err)
				}
			}
			if asc85 {
				s, err = re85(s)
				if err != nil {
					// ditto
					return nil, fmt.Errorf("error Ascii85ing string: %s", err)
				}
			}

			out.WriteString(s)
			zipped = false
			asc85 = false

		} else {

			if i.Typ == pdflex.ItemName && i.Val == "/FlateDecode" {
				zipped = true
			}
			if i.Typ == pdflex.ItemName && i.Val == "/ASCII85Decode" {
				asc85 = true
			}
			out.WriteString(i.Val)
		}

		if i.Typ == pdflex.ItemError {
			break
		}
	}
	return out.Bytes(), nil
}

func fix(in []byte) []byte {
	p := pdflex.Parser{Lexer: pdflex.NewLexer("", string(in))}
	return p.FixXrefs()
}

func shrinkWorker(in <-chan string, wg *sync.WaitGroup) {
	defer wg.Done()
	for arg := range in {
		log.Printf("[SHRINKING] %s\n", arg)

		// Read in
		raw, err := ioutil.ReadFile(arg)
		if err != nil {
			log.Printf("[SKIPPED] %s - %s\n", arg, err)
			continue
		}

		// Shrink
		shrunk, err := shrink(raw, *flagMax)
		if err != nil {
			log.Printf("[SKIPPED] %s - strict mode: %s\n", arg, err)
			continue
		}

		// Fix up xrefs
		fixed := fix(shrunk)

		// Write out
		newfn := strings.TrimSuffix(path.Base(arg), path.Ext(arg)) + "-small" + path.Ext(arg)
		newfn = path.Join(path.Dir(arg), newfn)
		err = ioutil.WriteFile(newfn, fixed, 0600)
		if err != nil {
			log.Printf("[SKIPPED] %s - %s", newfn, err)
		}
	}
}

func main() {

	flag.Usage = func() {
		fmt.Fprintf(
			os.Stderr,
			"  Usage: %s file [file file ...]\n"+
				"    -max=128: Trim streams whose size is greater than this value\n"+
				"    -strict=false: Abort on xref parsing errors etc\n"+
				"    -workers=1: Number of concurrent workers to use\n",
			path.Base(os.Args[0]),
		)
	}

	flag.Parse()
	if *flagMax < 0 {
		flag.Usage()
		os.Exit(1)
	}

	if *flagWorkers > runtime.NumCPU()*2 {
		fmt.Fprintf(
			os.Stderr,
			"Maximum sensible workers is cores * 2 (%d). (You tried %d)\n",
			runtime.NumCPU()*2,
			*flagWorkers,
		)
	}

	runtime.GOMAXPROCS(*flagWorkers)
	wg := &sync.WaitGroup{}
	work := make(chan string)
	for i := 0; i < *flagWorkers; i++ {
		wg.Add(1)
		go shrinkWorker(work, wg)
	}

	for _, arg := range flag.Args() {
		work <- arg
	}
	close(work)

	wg.Wait()

}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat"
	"github.com/loadimpact/speedboat/js"
	"github.com/loadimpact/speedboat/sampler"
	"github.com/loadimpact/speedboat/sampler/influxdb"
	"github.com/loadimpact/speedboat/sampler/stream"
	"github.com/loadimpact/speedboat/simple"
	"github.com/urfave/cli"
	"golang.org/x/net/context"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"
)

const (
	typeURL = "url"
	typeYML = "yml"
	typeJS  = "js"
)

// Configure the global logger.
func configureLogging(c *cli.Context) {
	log.SetLevel(log.InfoLevel)
	if c.GlobalBool("verbose") {
		log.SetLevel(log.DebugLevel)
	}
}

// Configure the global sampler.
func configureSampler(c *cli.Context) {
	sampler.DefaultSampler.OnError = func(err error) {
		log.WithError(err).Error("[Sampler error]")
	}

	for _, output := range c.GlobalStringSlice("metrics") {
		parts := strings.SplitN(output, "+", 2)
		switch parts[0] {
		case "influxdb":
			out, err := influxdb.NewFromURL(parts[1])
			if err != nil {
				log.WithError(err).Fatal("Couldn't create InfluxDB client")
			}
			sampler.DefaultSampler.Outputs = append(sampler.DefaultSampler.Outputs, out)
		default:
			var writer io.WriteCloser
			switch output {
			case "stdout", "-":
				writer = os.Stdout
			default:
				file, err := os.Create(output)
				if err != nil {
					log.WithError(err).Fatal("Couldn't create output file")
				}
				writer = file
			}

			var out sampler.Output
			switch c.GlobalString("format") {
			case "json":
				out = &stream.JSONOutput{Output: writer}
			case "csv":
				out = &stream.CSVOutput{Output: writer}
			default:
				log.Fatal("Unknown output format")
			}
			sampler.DefaultSampler.Outputs = append(sampler.DefaultSampler.Outputs, out)
		}
	}
}

func guessType(arg string) string {
	switch {
	case strings.Contains(arg, "://"):
		return typeURL
	case strings.HasSuffix(arg, ".js"):
		return typeJS
	case strings.HasSuffix(arg, ".yml"):
		return typeYML
	}
	return ""
}

func parse(cc *cli.Context) (conf Config, err error) {
	if len(cc.Args()) == 0 {
		return conf, errors.New("Nothing to do!")
	}

	conf.VUs = cc.Int("vus")
	conf.Duration = cc.Duration("duration").String()

	arg := cc.Args()[0]
	argType := cc.String("type")
	if argType == "" {
		argType = guessType(arg)
	}

	switch argType {
	case typeYML:
		bytes, err := ioutil.ReadFile(cc.Args()[0])
		if err != nil {
			return conf, errors.New("Couldn't read config file")
		}
		if err := yaml.Unmarshal(bytes, &conf); err != nil {
			return conf, errors.New("Couldn't parse config file")
		}
	case typeURL:
		conf.URL = arg
	case typeJS:
		conf.Script = arg
	default:
		return conf, errors.New("Unsure of what to do, try specifying --type")
	}

	return conf, nil
}

func headlessController(c context.Context, t *speedboat.Test) <-chan int {
	ch := make(chan int)

	go func() {
		defer close(ch)

		select {
		case ch <- t.VUsAt(0):
		case <-c.Done():
			return
		}

		startTime := time.Now()
		ticker := time.NewTicker(100 * time.Millisecond)
		for {
			select {
			case <-ticker.C:
				ch <- t.VUsAt(time.Since(startTime))
			case <-c.Done():
				return
			}
		}
	}()

	return ch
}

func action(cc *cli.Context) error {
	if len(cc.Args()) == 0 {
		cli.ShowAppHelp(cc)
		return nil
	}

	conf, err := parse(cc)
	if err != nil {
		log.WithError(err).Fatal("Invalid arguments; see --help")
	}

	t, err := conf.MakeTest()
	if err != nil {
		log.WithError(err).Fatal("Configuration error")
	}

	// Inspect the test to find a suitable runner; additional ones can easily be added
	var runner speedboat.Runner
	switch {
	case t.Script == "":
		runner = simple.New()
	case strings.HasSuffix(t.Script, ".js"):
		src, err := ioutil.ReadFile(t.Script)
		if err != nil {
			log.WithError(err).Fatal("Couldn't read script")
		}
		runner = js.New(t.Script, string(src))
	default:
		log.Fatal("No suitable runner found!")
	}

	// Context that expires at the end of the test
	ctx, cancel := context.WithTimeout(context.Background(), t.TotalDuration())

	// Configure the VU logger
	logger := &log.Logger{
		Out:       os.Stderr,
		Level:     log.DebugLevel,
		Formatter: &log.TextFormatter{},
	}
	ctx = speedboat.WithLogger(ctx, logger)

	// Store metrics unless the --quiet flag is specified
	quiet := cc.Bool("quiet")
	sampler.DefaultSampler.Accumulate = !quiet

	// Commit metrics to any configured backends once per second
	go func() {
		ticker := time.NewTicker(1 * time.Second)
		for {
			select {
			case <-ticker.C:
				commitMetrics()
			case <-ctx.Done():
				return
			}
		}
	}()

	// Use a "headless controller" to scale VUs by polling the test ramp
	mVUs := sampler.Gauge("vus")
	vus := []context.CancelFunc{}
	for scale := range headlessController(ctx, &t) {
		for i := len(vus); i < scale; i++ {
			log.WithField("id", i).Debug("Spawning VU")
			vuCtx, vuCancel := context.WithCancel(ctx)
			vus = append(vus, vuCancel)
			go func() {
				defer func() {
					if v := recover(); v != nil {
						switch err := v.(type) {
						case speedboat.FlowControl:
							switch err {
							case speedboat.AbortTest:
								log.Error("Test aborted")
								cancel()
							}
						default:
							log.WithFields(log.Fields{
								"id":    i,
								"error": err,
							}).Error("VU crashed!")
						}
					}
				}()
				runner.RunVU(vuCtx, t, len(vus))
			}()
		}
		for i := len(vus); i > scale; i-- {
			log.WithField("id", i-1).Debug("Dropping VU")
			vus[i-1]()
			vus = vus[:i-1]
		}
		mVUs.Int(len(vus))
	}

	// Wait until the end of the test
	<-ctx.Done()

	// Print and commit final metrics
	if !quiet {
		printMetrics()
	}
	commitMetrics()
	closeMetrics()

	return nil
}

func main() {
	// Free up -v and -h for our own flags
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help, ?"

	// Bootstrap using action-registered commandline flags
	app := cli.NewApp()
	app.Name = "speedboat"
	app.Usage = "A next-generation load generator"
	app.Version = "0.0.1a1"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "type, t",
			Usage: "Input file type, if not evident (url, yml or js)",
		},
		cli.IntFlag{
			Name:  "vus, u",
			Usage: "Number of VUs to simulate",
			Value: 10,
		},
		cli.DurationFlag{
			Name:  "duration, d",
			Usage: "Test duration",
			Value: time.Duration(10) * time.Second,
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "More verbose output",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "Suppress the summary at the end of a test",
		},
		cli.StringSliceFlag{
			Name:  "metrics, m",
			Usage: "Write metrics to a file or database",
		},
		cli.StringFlag{
			Name:  "format, f",
			Usage: "Metric output format (json or csv)",
			Value: "json",
		},
	}
	app.Before = func(c *cli.Context) error {
		configureLogging(c)
		configureSampler(c)
		return nil
	}
	app.Action = action
	app.Run(os.Args)
}
<commit_msg>v1.0.0-mvp1<commit_after>package main

import (
	"errors"
	log "github.com/Sirupsen/logrus"
	"github.com/loadimpact/speedboat"
	"github.com/loadimpact/speedboat/js"
	"github.com/loadimpact/speedboat/sampler"
	"github.com/loadimpact/speedboat/sampler/influxdb"
	"github.com/loadimpact/speedboat/sampler/stream"
	"github.com/loadimpact/speedboat/simple"
	"github.com/urfave/cli"
	"golang.org/x/net/context"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"
)

const (
	typeURL = "url"
	typeYML = "yml"
	typeJS  = "js"
)

// Configure the global logger.
func configureLogging(c *cli.Context) {
	log.SetLevel(log.InfoLevel)
	if c.GlobalBool("verbose") {
		log.SetLevel(log.DebugLevel)
	}
}

// Configure the global sampler.
func configureSampler(c *cli.Context) {
	sampler.DefaultSampler.OnError = func(err error) {
		log.WithError(err).Error("[Sampler error]")
	}

	for _, output := range c.GlobalStringSlice("metrics") {
		parts := strings.SplitN(output, "+", 2)
		switch parts[0] {
		case "influxdb":
			out, err := influxdb.NewFromURL(parts[1])
			if err != nil {
				log.WithError(err).Fatal("Couldn't create InfluxDB client")
			}
			sampler.DefaultSampler.Outputs = append(sampler.DefaultSampler.Outputs, out)
		default:
			var writer io.WriteCloser
			switch output {
			case "stdout", "-":
				writer = os.Stdout
			default:
				file, err := os.Create(output)
				if err != nil {
					log.WithError(err).Fatal("Couldn't create output file")
				}
				writer = file
			}

			var out sampler.Output
			switch c.GlobalString("format") {
			case "json":
				out = &stream.JSONOutput{Output: writer}
			case "csv":
				out = &stream.CSVOutput{Output: writer}
			default:
				log.Fatal("Unknown output format")
			}
			sampler.DefaultSampler.Outputs = append(sampler.DefaultSampler.Outputs, out)
		}
	}
}

func guessType(arg string) string {
	switch {
	case strings.Contains(arg, "://"):
		return typeURL
	case strings.HasSuffix(arg, ".js"):
		return typeJS
	case strings.HasSuffix(arg, ".yml"):
		return typeYML
	}
	return ""
}

func parse(cc *cli.Context) (conf Config, err error) {
	if len(cc.Args()) == 0 {
		return conf, errors.New("Nothing to do!")
	}

	conf.VUs = cc.Int("vus")
	conf.Duration = cc.Duration("duration").String()

	arg := cc.Args()[0]
	argType := cc.String("type")
	if argType == "" {
		argType = guessType(arg)
	}

	switch argType {
	case typeYML:
		bytes, err := ioutil.ReadFile(cc.Args()[0])
		if err != nil {
			return conf, errors.New("Couldn't read config file")
		}
		if err := yaml.Unmarshal(bytes, &conf); err != nil {
			return conf, errors.New("Couldn't parse config file")
		}
	case typeURL:
		conf.URL = arg
	case typeJS:
		conf.Script = arg
	default:
		return conf, errors.New("Unsure of what to do, try specifying --type")
	}

	return conf, nil
}

func headlessController(c context.Context, t *speedboat.Test) <-chan int {
	ch := make(chan int)

	go func() {
		defer close(ch)

		select {
		case ch <- t.VUsAt(0):
		case <-c.Done():
			return
		}

		startTime := time.Now()
		ticker := time.NewTicker(100 * time.Millisecond)
		for {
			select {
			case <-ticker.C:
				ch <- t.VUsAt(time.Since(startTime))
			case <-c.Done():
				return
			}
		}
	}()

	return ch
}

func action(cc *cli.Context) error {
	if len(cc.Args()) == 0 {
		cli.ShowAppHelp(cc)
		return nil
	}

	conf, err := parse(cc)
	if err != nil {
		log.WithError(err).Fatal("Invalid arguments; see --help")
	}

	t, err := conf.MakeTest()
	if err != nil {
		log.WithError(err).Fatal("Configuration error")
	}

	// Inspect the test to find a suitable runner; additional ones can easily be added
	var runner speedboat.Runner
	switch {
	case t.Script == "":
		runner = simple.New()
	case strings.HasSuffix(t.Script, ".js"):
		src, err := ioutil.ReadFile(t.Script)
		if err != nil {
			log.WithError(err).Fatal("Couldn't read script")
		}
		runner = js.New(t.Script, string(src))
	default:
		log.Fatal("No suitable runner found!")
	}

	// Context that expires at the end of the test
	ctx, cancel := context.WithTimeout(context.Background(), t.TotalDuration())

	// Configure the VU logger
	logger := &log.Logger{
		Out:       os.Stderr,
		Level:     log.DebugLevel,
		Formatter: &log.TextFormatter{},
	}
	ctx = speedboat.WithLogger(ctx, logger)

	// Store metrics unless the --quiet flag is specified
	quiet := cc.Bool("quiet")
	sampler.DefaultSampler.Accumulate = !quiet

	// Commit metrics to any configured backends once per second
	go func() {
		ticker := time.NewTicker(1 * time.Second)
		for {
			select {
			case <-ticker.C:
				commitMetrics()
			case <-ctx.Done():
				return
			}
		}
	}()

	// Use a "headless controller" to scale VUs by polling the test ramp
	mVUs := sampler.Gauge("vus")
	vus := []context.CancelFunc{}
	for scale := range headlessController(ctx, &t) {
		for i := len(vus); i < scale; i++ {
			log.WithField("id", i).Debug("Spawning VU")
			vuCtx, vuCancel := context.WithCancel(ctx)
			vus = append(vus, vuCancel)
			go func() {
				defer func() {
					if v := recover(); v != nil {
						switch err := v.(type) {
						case speedboat.FlowControl:
							switch err {
							case speedboat.AbortTest:
								log.Error("Test aborted")
								cancel()
							}
						default:
							log.WithFields(log.Fields{
								"id":    i,
								"error": err,
							}).Error("VU crashed!")
						}
					}
				}()
				runner.RunVU(vuCtx, t, len(vus))
			}()
		}
		for i := len(vus); i > scale; i-- {
			log.WithField("id", i-1).Debug("Dropping VU")
			vus[i-1]()
			vus = vus[:i-1]
		}
		mVUs.Int(len(vus))
	}

	// Wait until the end of the test
	<-ctx.Done()

	// Print and commit final metrics
	if !quiet {
		printMetrics()
	}
	commitMetrics()
	closeMetrics()

	return nil
}

func main() {
	// Free up -v and -h for our own flags
	cli.VersionFlag.Name = "version"
	cli.HelpFlag.Name = "help, ?"

	// Bootstrap using action-registered commandline flags
	app := cli.NewApp()
	app.Name = "speedboat"
	app.Usage = "A next-generation load generator"
	app.Version = "1.0.0-mvp1"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "type, t",
			Usage: "Input file type, if not evident (url, yml or js)",
		},
		cli.IntFlag{
			Name:  "vus, u",
			Usage: "Number of VUs to simulate",
			Value: 10,
		},
		cli.DurationFlag{
			Name:  "duration, d",
			Usage: "Test duration",
			Value: time.Duration(10) * time.Second,
		},
		cli.BoolFlag{
			Name:  "verbose, v",
			Usage: "More verbose output",
		},
		cli.BoolFlag{
			Name:  "quiet, q",
			Usage: "Suppress the summary at the end of a test",
		},
		cli.StringSliceFlag{
			Name:  "metrics, m",
			Usage: "Write metrics to a file or database",
		},
		cli.StringFlag{
			Name:  "format, f",
			Usage: "Metric output format (json or csv)",
			Value: "json",
		},
	}
	app.Before = func(c *cli.Context) error {
		configureLogging(c)
		configureSampler(c)
		return nil
	}
	app.Action = action
	app.Run(os.Args)
}
<|endoftext|>


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

package main

import (
	"github.com/mholt/caddy/caddy/caddymain"

	_ "github.com/SchumacherFM/mailout"
	_ "github.com/captncraig/caddy-realip"
	_ "github.com/miekg/caddy-prometheus"
	_ "github.com/txtdirect/txtdirect"
)

func main() {
	caddymain.EnableTelemetry = false
	caddymain.Run()
}<commit_msg>Fix whitespace<commit_after>/*
Copyright 2019 - The TXTDirect Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"github.com/mholt/caddy/caddy/caddymain"

	_ "github.com/SchumacherFM/mailout"
	_ "github.com/captncraig/caddy-realip"
	_ "github.com/miekg/caddy-prometheus"
	_ "github.com/txtdirect/txtdirect"
)

func main() {
	caddymain.EnableTelemetry = false
	caddymain.Run()
}<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/garyburd/redigo/redis"
	"github.com/gocraft/work/webui"
	"os"
	"os/signal"
	"time"
)

var redisHostPort = flag.String("redis", ":6379", "redis hostport")
var redisNamespace = flag.String("ns", "work", "redis namespace")
var webHostPort = flag.String("listen", ":5040", "hostport to listen for HTTP JSON API")

func main() {
	flag.Parse()

	fmt.Println("Starting workwebui:")
	fmt.Println("redis = ", *redisHostPort)
	fmt.Println("namespace = ", *redisNamespace)
	fmt.Println("listen = ", *webHostPort)

	pool := newPool(*redisHostPort)

	server := webui.NewServer(*redisNamespace, pool, *webHostPort)
	server.Start()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	<-c

	server.Stop()

	fmt.Println("\nQuitting...")
}

func newPool(addr string) *redis.Pool {
	return &redis.Pool{
		MaxActive:   3,
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", addr)
		},
		Wait: true,
	}
}
<commit_msg>Adding the parameter database for the redis pool<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"time"

	"github.com/garyburd/redigo/redis"
	"github.com/gocraft/work/webui"
)

var (
	redisHostPort  = flag.String("redis", ":6379", "redis hostport")
	redisDatabase  = flag.String("database", "0", "redis database")
	redisNamespace = flag.String("ns", "work", "redis namespace")
	webHostPort    = flag.String("listen", ":5040", "hostport to listen for HTTP JSON API")
)

func main() {
	flag.Parse()

	fmt.Println("Starting workwebui:")
	fmt.Println("redis = ", *redisHostPort)
	fmt.Println("database = ", *redisDatabase)
	fmt.Println("namespace = ", *redisNamespace)
	fmt.Println("listen = ", *webHostPort)

	database, err := strconv.Atoi(*redisDatabase)
	if err != nil {
		fmt.Printf("Error: %v is not a valid database value", *redisDatabase)
		return
	}

	pool := newPool(*redisHostPort, database)

	server := webui.NewServer(*redisNamespace, pool, *webHostPort)
	server.Start()

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt, os.Kill)

	<-c

	server.Stop()

	fmt.Println("\nQuitting...")
}

func newPool(addr string, database int) *redis.Pool {
	return &redis.Pool{
		MaxActive:   3,
		MaxIdle:     3,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			return redis.Dial("tcp", addr, redis.DialDatabase(database))
		},
		Wait: true,
	}
}
<|endoftext|>


<commit_before>package miniredis

import (
	"testing"

	"github.com/garyburd/redigo/redis"
)

func TestCmdEvalReplyConversion(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)

	cases := map[string]struct {
		script   string
		args     []interface{}
		expected interface{}
	}{
		"Return nil": {
			script: "",
			args: []interface{}{
				0,
			},
		},
		"Return boolean true": {
			script: "return true",
			args: []interface{}{
				0,
			},
			expected: int64(1),
		},
		"Return boolean false": {
			script: "return true",
			args: []interface{}{
				0,
			},
			expected: int64(1),
		},
		"Return single number": {
			script: "return 10",
			args: []interface{}{
				0,
			},
			expected: int64(10),
		},
		"Return single float": {
			script: "return 12.345",
			args: []interface{}{
				0,
			},
			expected: int64(12),
		},
		"Return multiple number": {
			script: "return 10, 20",
			args: []interface{}{
				0,
			},
			expected: int64(10),
		},
		"Return single string": {
			script: "return 'test'",
			args: []interface{}{
				0,
			},
			expected: "test",
		},
		"Return multiple string": {
			script: "return 'test1', 'test2'",
			args: []interface{}{
				0,
			},
			expected: "test1",
		},
		"Return single table multiple integer": {
			script: "return {10, 20}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				int64(10),
				int64(20),
			},
		},
		"Return single table multiple string": {
			script: "return {'test1', 'test2'}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				"test1",
				"test2",
			},
		},
		"Return nested table": {
			script: "return {10, 20, {30, 40}}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				int64(10),
				int64(20),
				[]interface{}{
					int64(30),
					int64(40),
				},
			},
		},
		"Return combination table": {
			script: "return {10, 20, {30, 'test', true, 40}, false}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				int64(10),
				int64(20),
				[]interface{}{
					int64(30),
					"test",
					int64(1),
					int64(40),
				},
				int64(0),
			},
		},
		"KEYS and ARGV": {
			script: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
			args: []interface{}{
				2,
				"key1",
				"key2",
				"first",
				"second",
			},
			expected: []interface{}{
				"key1",
				"key2",
				"first",
				"second",
			},
		},
	}

	for id, tc := range cases {
		args := make([]interface{}, len(tc.args)+1)
		args[0] = tc.script
		for index, arg := range tc.args {
			args[index+1] = arg
		}

		reply, err := c.Do("EVAL", args...)
		if err != nil {
			t.Errorf("%v: Unexpected error: %v", id, err)
		}

		equals(t, tc.expected, reply)
	}
}

func TestCmdEvalResponse(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	{
		v, err := c.Do("EVAL", "return redis.call('set','foo','bar')", 0)
		ok(t, err)
		equals(t, "OK", v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('get','foo')", 0)
		ok(t, err)
		equals(t, "bar", v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMSET', 'mkey', 'foo','bar','foo1','bar1')", 0)
		ok(t, err)
		equals(t, "OK", v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HGETALL','mkey')", 0)
		ok(t, err)
		equals(t, []interface{}{"foo", "bar", "foo1", "bar1"}, v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'foo1')", 0)
		ok(t, err)
		equals(t, []interface{}{"bar1"}, v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'foo')", 0)
		ok(t, err)
		equals(t, []interface{}{"bar"}, v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'bad', 'key')", 0)
		ok(t, err)
		equals(t, []interface{}{nil, nil}, v)
	}
}

func TestCmdScript(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.Strings(c.Do("SCRIPT", "LOAD", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", "return redis.call('set','foo','bar')"))
		ok(t, err)
		equals(t, []string{"a42059b356c875f0717db19a51f6aaca9ae659ea", "2fa2b029f72572e803ff55a09b1282699aecae6a"}, v)
	}

	// SCRIPT EXISTS
	{
		v, err := redis.Int64s(c.Do("SCRIPT", "exists", "a42059b356c875f0717db19a51f6aaca9ae659ea", "2fa2b029f72572e803ff55a09b1282699aecae6a", "invalid sha"))
		ok(t, err)
		equals(t, []int64{1, 1, 0}, v)
	}

	// SCRIPT FLUSH
	{
		v, err := redis.String(c.Do("SCRIPT", "flush"))
		ok(t, err)
		equals(t, "OK", v)
	}
}

func TestCmdScriptAndEvalsha(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.Strings(c.Do("SCRIPT", "LOAD", "redis.call('set', KEYS[1], ARGV[1])\n return redis.call('get', KEYS[1]) "))
		ok(t, err)
		equals(t, []string{"054a13c20b748da2922a5f37f144342de21b8650"}, v)
	}

	// TEST EVALSHA
	{
		v, err := c.Do("EVALSHA", "054a13c20b748da2922a5f37f144342de21b8650", 1, "test_key", "test_value")
		ok(t, err)
		equals(t, "test_value", v)
	}

}

func TestCmdScriptAndEvalshaErrorRedisCall(t *testing.T) {
	c, err := redis.Dial("tcp", "127.0.0.1:6379")
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.String(c.Do("EVAL", "return redis.call('invalid', 'key', 'value') ", 0))
		ok(t, err)
		equals(t, "6a5ccb5fcaf42edce7f9bcb529e58d0f5c2d97c4", v)
	}
}
func TestCmdScriptAndEvalshaErrorRedisPCall(t *testing.T) {
	c, err := redis.Dial("tcp", "127.0.0.1:6379")
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.String(c.Do("EVAL", "return redis.pcall('invalid', 'key', 'value') ", 0))
		ok(t, err)
		equals(t, "6a5ccb5fcaf42edce7f9bcb529e58d0f5c2d97c4", v)
	}
}

func TestCmdScriptAndEvalshaError(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.String(c.Do("EVAL", "return redis.call('invalid', 'key', 'value') ", 0))
		ok(t, err)
		equals(t, "6a5ccb5fcaf42edce7f9bcb529e58d0f5c2d97c4", v)
	}

	// SCRIPT LOAD
	{
		v, err := redis.String(c.Do("EVAL", "return redis.pcall('invalid', 'key', 'value') ", 0))
		ok(t, err)
		equals(t, "6a5ccb5fcaf42edce7f9bcb529e58d0f5c2d97c4", v)
	}

}
<commit_msg>Removed unnecessary test code<commit_after>package miniredis

import (
	"testing"

	"github.com/garyburd/redigo/redis"
)

func TestCmdEvalReplyConversion(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)

	cases := map[string]struct {
		script   string
		args     []interface{}
		expected interface{}
	}{
		"Return nil": {
			script: "",
			args: []interface{}{
				0,
			},
		},
		"Return boolean true": {
			script: "return true",
			args: []interface{}{
				0,
			},
			expected: int64(1),
		},
		"Return boolean false": {
			script: "return true",
			args: []interface{}{
				0,
			},
			expected: int64(1),
		},
		"Return single number": {
			script: "return 10",
			args: []interface{}{
				0,
			},
			expected: int64(10),
		},
		"Return single float": {
			script: "return 12.345",
			args: []interface{}{
				0,
			},
			expected: int64(12),
		},
		"Return multiple number": {
			script: "return 10, 20",
			args: []interface{}{
				0,
			},
			expected: int64(10),
		},
		"Return single string": {
			script: "return 'test'",
			args: []interface{}{
				0,
			},
			expected: "test",
		},
		"Return multiple string": {
			script: "return 'test1', 'test2'",
			args: []interface{}{
				0,
			},
			expected: "test1",
		},
		"Return single table multiple integer": {
			script: "return {10, 20}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				int64(10),
				int64(20),
			},
		},
		"Return single table multiple string": {
			script: "return {'test1', 'test2'}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				"test1",
				"test2",
			},
		},
		"Return nested table": {
			script: "return {10, 20, {30, 40}}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				int64(10),
				int64(20),
				[]interface{}{
					int64(30),
					int64(40),
				},
			},
		},
		"Return combination table": {
			script: "return {10, 20, {30, 'test', true, 40}, false}",
			args: []interface{}{
				0,
			},
			expected: []interface{}{
				int64(10),
				int64(20),
				[]interface{}{
					int64(30),
					"test",
					int64(1),
					int64(40),
				},
				int64(0),
			},
		},
		"KEYS and ARGV": {
			script: "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}",
			args: []interface{}{
				2,
				"key1",
				"key2",
				"first",
				"second",
			},
			expected: []interface{}{
				"key1",
				"key2",
				"first",
				"second",
			},
		},
	}

	for id, tc := range cases {
		args := make([]interface{}, len(tc.args)+1)
		args[0] = tc.script
		for index, arg := range tc.args {
			args[index+1] = arg
		}

		reply, err := c.Do("EVAL", args...)
		if err != nil {
			t.Errorf("%v: Unexpected error: %v", id, err)
		}

		equals(t, tc.expected, reply)
	}
}

func TestCmdEvalResponse(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	{
		v, err := c.Do("EVAL", "return redis.call('set','foo','bar')", 0)
		ok(t, err)
		equals(t, "OK", v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('get','foo')", 0)
		ok(t, err)
		equals(t, "bar", v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMSET', 'mkey', 'foo','bar','foo1','bar1')", 0)
		ok(t, err)
		equals(t, "OK", v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HGETALL','mkey')", 0)
		ok(t, err)
		equals(t, []interface{}{"foo", "bar", "foo1", "bar1"}, v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'foo1')", 0)
		ok(t, err)
		equals(t, []interface{}{"bar1"}, v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'foo')", 0)
		ok(t, err)
		equals(t, []interface{}{"bar"}, v)
	}

	{
		v, err := c.Do("EVAL", "return redis.call('HMGET','mkey', 'bad', 'key')", 0)
		ok(t, err)
		equals(t, []interface{}{nil, nil}, v)
	}
}

func TestCmdScript(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.Strings(c.Do("SCRIPT", "LOAD", "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}", "return redis.call('set','foo','bar')"))
		ok(t, err)
		equals(t, []string{"a42059b356c875f0717db19a51f6aaca9ae659ea", "2fa2b029f72572e803ff55a09b1282699aecae6a"}, v)
	}

	// SCRIPT EXISTS
	{
		v, err := redis.Int64s(c.Do("SCRIPT", "exists", "a42059b356c875f0717db19a51f6aaca9ae659ea", "2fa2b029f72572e803ff55a09b1282699aecae6a", "invalid sha"))
		ok(t, err)
		equals(t, []int64{1, 1, 0}, v)
	}

	// SCRIPT FLUSH
	{
		v, err := redis.String(c.Do("SCRIPT", "flush"))
		ok(t, err)
		equals(t, "OK", v)
	}
}

func TestCmdScriptAndEvalsha(t *testing.T) {
	s, err := Run()
	ok(t, err)
	defer s.Close()

	c, err := redis.Dial("tcp", s.Addr())
	ok(t, err)
	defer c.Close()

	// SCRIPT LOAD
	{
		v, err := redis.Strings(c.Do("SCRIPT", "LOAD", "redis.call('set', KEYS[1], ARGV[1])\n return redis.call('get', KEYS[1]) "))
		ok(t, err)
		equals(t, []string{"054a13c20b748da2922a5f37f144342de21b8650"}, v)
	}

	// TEST EVALSHA
	{
		v, err := c.Do("EVALSHA", "054a13c20b748da2922a5f37f144342de21b8650", 1, "test_key", "test_value")
		ok(t, err)
		equals(t, "test_value", v)
	}

}
<|endoftext|>


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

package main

import (
	"log"
	"os/exec"
)

func main() {
	NewUpspinServer().Start()
}
<commit_msg>cmds/upspin/upspin.go: removed unused dependencies<commit_after>// Copyright 2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

func main() {
	NewUpspinServer().Start()
}
<|endoftext|>


<commit_before>package excase

import (
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/loadoff/excl"
)

type exStyles struct {
	style1    *excl.Style
	style1New *excl.Style
	style2    *excl.Style
	style2New *excl.Style
	style3    *excl.Style
	style3New *excl.Style
	style4    *excl.Style
	style4New *excl.Style
	style5    *excl.Style
	style5New *excl.Style
	style6    *excl.Style
	style6New *excl.Style
	style7    *excl.Style
	style8    *excl.Style
	style9    *excl.Style
	style10   *excl.Style
	style11   *excl.Style
	style12   *excl.Style
	style13   *excl.Style
	style14   *excl.Style
	style15   *excl.Style
	style16   *excl.Style
	style17   *excl.Style
}

// ExCase テストケース
type ExCase struct {
	FilePath string
	caseBook *excl.Workbook
	dir      string
	styles   *exStyles
	sections []*ExSection
}

// ExSection セクション情報
type ExSection struct {
	testCount   int
	largeCount  int
	middleCount int
	smallCount  int
	styles      *exStyles
	caseSheet   *excl.Sheet
	name        string
	large       string
	middle      string
	small       string
}

type ExTest struct {
	row *excl.Row
}

// InitExCase Excelテストケースを作成する
func InitExCase() *ExCase {
	var err error
	ex := &ExCase{}
	ex.FilePath = strings.Replace(time.Now().Format("20060102030405"), ".", "", 1) + ".xlsx"
	ex.dir, err = ioutil.TempDir("", "expand"+strings.Replace(time.Now().Format("20060102030405"), ".", "", 1))

	if ex.caseBook, err = excl.CreateWorkbook(ex.dir, ex.FilePath); err != nil {
		fmt.Println(err.Error())
		return nil
	}
	// スタイル作成
	style := &excl.Style{Wrap: 1, Vertical: "top"}
	border := excl.Border{Left: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	style.Wrap = 1
	ex.styles = &exStyles{}
	ex.styles.style1 = style
	ex.styles.style3 = style
	ex.styles.style5 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "hair"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	style.Wrap = 1
	ex.styles.style2 = style
	ex.styles.style4 = style
	ex.styles.style6 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "thin"}, Top: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	style.Wrap = 1
	ex.styles.style1New = style
	ex.styles.style3New = style
	ex.styles.style5New = style
	ex.styles.style7 = style
	ex.styles.style9 = style
	ex.styles.style10 = style
	ex.styles.style11 = style
	ex.styles.style12 = style
	ex.styles.style13 = style
	ex.styles.style14 = style
	ex.styles.style15 = style
	ex.styles.style16 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "thin"}, Top: &excl.BorderSetting{Style: "thin"}, Right: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	ex.styles.style17 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "hair"}, Top: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	ex.styles.style2New = style
	ex.styles.style4New = style
	ex.styles.style6New = style
	ex.styles.style8 = style
	return ex
}

// OpenSection 新しいシートにテストを出力する
func (ex *ExCase) OpenSection(name string) *ExSection {
	for _, sec := range ex.sections {
		if sec.name == name {
			return sec
		}
	}
	sec := &ExSection{name: name, styles: ex.styles}
	sec.caseSheet, _ = ex.caseBook.OpenSheet(name)
	sec.caseSheet.ShowGridlines(false)
	caseRow := sec.caseSheet.GetRow(4)
	borderSetting := &excl.BorderSetting{Style: "thin"}
	border := excl.Border{Left: borderSetting, Right: borderSetting, Top: borderSetting, Bottom: borderSetting}
	font := excl.Font{Color: "FFFFFF"}
	style := &excl.Style{}
	style.FontID = ex.caseBook.Styles.SetFont(font)
	style.FillID = ex.caseBook.Styles.SetBackgroundColor("361e6d")
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	caseRow.SetString("No.", 1).SetStyle(style)
	caseRow.SetString("大項目名", 2).SetStyle(style)
	caseRow.SetString("No.", 3).SetStyle(style)
	caseRow.SetString("中項目名", 4).SetStyle(style)
	caseRow.SetString("No.", 5).SetStyle(style)
	caseRow.SetString("小項目名", 6).SetStyle(style)
	caseRow.SetString("No.", 7).SetStyle(style)
	caseRow.SetString("実施内容", 8).SetStyle(style)
	caseRow.SetString("合格条件", 9).SetStyle(style)
	caseRow.SetString("実施日", 10).SetStyle(style)
	caseRow.SetString("実施者", 11).SetStyle(style)
	caseRow.SetString("結果", 12).SetStyle(style)
	caseRow.SetString("補足", 13).SetStyle(style)
	caseRow.SetString("エビデンス", 14).SetStyle(style)
	caseRow.SetString("検証日", 15).SetStyle(style)
	caseRow.SetString("検証者", 16).SetStyle(style)
	caseRow.SetString("結果", 17).SetStyle(style)
	ex.sections = append(ex.sections, sec)
	return sec
}

// CloseSection セクションを閉じる
func (ex *ExSection) CloseSection() {
	if ex.caseSheet == nil {
		return
	}
	caseRow := ex.caseSheet.GetRow(ex.testCount + 5)
	border := excl.Border{Top: &excl.BorderSetting{Style: "thin"}}
	style := &excl.Style{}
	style.BorderID = ex.caseSheet.Styles.SetBorder(border)
	caseRow.GetCell(1).SetStyle(style)
	caseRow.GetCell(2).SetStyle(style)
	caseRow.GetCell(3).SetStyle(style)
	caseRow.GetCell(4).SetStyle(style)
	caseRow.GetCell(5).SetStyle(style)
	caseRow.GetCell(6).SetStyle(style)
	caseRow.GetCell(7).SetStyle(style)
	caseRow.GetCell(8).SetStyle(style)
	caseRow.GetCell(9).SetStyle(style)
	caseRow.GetCell(10).SetStyle(style)
	caseRow.GetCell(11).SetStyle(style)
	caseRow.GetCell(12).SetStyle(style)
	caseRow.GetCell(13).SetStyle(style)
	caseRow.GetCell(14).SetStyle(style)
	caseRow.GetCell(15).SetStyle(style)
	caseRow.GetCell(16).SetStyle(style)
	caseRow.GetCell(17).SetStyle(style)
	ex.caseSheet.Close()
	ex.caseSheet = nil
}

// Close 閉じる
func (ex *ExCase) Close() {
	for _, sec := range ex.sections {
		sec.CloseSection()
	}
	ex.caseBook.Close()
	os.RemoveAll(ex.dir)
}

// Large 大項目をセット
func (ex *ExSection) Large(name string) *ExSection {
	ex.largeCount++
	ex.middleCount = 0
	ex.smallCount = 0
	ex.large = name
	return ex
}

// Middle 中項目をセット
func (ex *ExSection) Middle(name string) *ExSection {
	ex.smallCount = 0
	ex.middleCount++
	ex.middle = name
	/*	*/
	return ex
}

// Small 小項目をセット
func (ex *ExSection) Small(name string) *ExSection {
	ex.smallCount++
	ex.small = name
	return ex
}

// Test テストの内容と合格条件をセットする
func (ex *ExSection) Test(content string, pass string) *ExTest {
	ex.testCount++
	test := &ExTest{}
	test.row = ex.caseSheet.GetRow(ex.testCount + 4)

	test.row.GetCell(1).SetStyle(ex.styles.style1)
	test.row.GetCell(2).SetStyle(ex.styles.style2)
	test.row.GetCell(3).SetStyle(ex.styles.style3)
	test.row.GetCell(4).SetStyle(ex.styles.style4)
	test.row.GetCell(5).SetStyle(ex.styles.style5)
	test.row.GetCell(6).SetStyle(ex.styles.style6)
	test.row.GetCell(7).SetStyle(ex.styles.style7)
	test.row.GetCell(8).SetStyle(ex.styles.style8)
	test.row.GetCell(9).SetStyle(ex.styles.style9)
	test.row.GetCell(10).SetStyle(ex.styles.style10)
	test.row.GetCell(11).SetStyle(ex.styles.style11)
	test.row.GetCell(12).SetStyle(ex.styles.style12)
	test.row.GetCell(13).SetStyle(ex.styles.style13)
	test.row.GetCell(14).SetStyle(ex.styles.style14)
	test.row.GetCell(15).SetStyle(ex.styles.style15)
	test.row.GetCell(16).SetStyle(ex.styles.style16)
	test.row.GetCell(17).SetStyle(ex.styles.style17)

	// 大項目処理
	if ex.large != "" {
		test.row.SetNumber(strconv.Itoa(ex.largeCount), 1).SetStyle(ex.styles.style1New)
		test.row.SetString(ex.large, 2).SetStyle(ex.styles.style2New)
		test.row.GetCell(3).SetStyle(ex.styles.style3New)
		test.row.GetCell(4).SetStyle(ex.styles.style4New)
		test.row.GetCell(5).SetStyle(ex.styles.style5New)
		test.row.GetCell(6).SetStyle(ex.styles.style6New)
		ex.large = ""
	}
	// 中項目処理
	if ex.middle != "" {
		test.row.SetNumber(strconv.Itoa(ex.middleCount), 3).SetStyle(ex.styles.style3New)
		test.row.SetString(ex.middle, 4).SetStyle(ex.styles.style4New)
		test.row.GetCell(5).SetStyle(ex.styles.style5New)
		test.row.GetCell(6).SetStyle(ex.styles.style6New)
		ex.middle = ""
	}
	// 小項目処理
	if ex.small != "" {
		test.row.SetNumber(strconv.Itoa(ex.smallCount), 5).SetStyle(ex.styles.style5New)
		test.row.SetString(ex.small, 6).SetStyle(ex.styles.style6New)
		ex.small = ""
	}
	test.row.SetNumber(strconv.Itoa(ex.testCount), 7)
	test.row.SetString(content, 8)
	test.row.SetString(pass, 9)
	return test
}

// Passed 合格をセット
func (test *ExTest) Passed() *ExTest {
	test.row.SetString(time.Now().Format("01/02"), 10)
	test.row.SetString("合格", 12)
	return test
}

// Failed 不合格をセット
func (test *ExTest) Failed() *ExTest {
	test.row.SetString(time.Now().Format("01/02"), 10)
	test.row.SetString("不合格", 12)

	test.row.GetCell(1).SetBackgroundColor("fb0a2a")
	test.row.GetCell(2).SetBackgroundColor("fb0a2a")
	test.row.GetCell(3).SetBackgroundColor("fb0a2a")
	test.row.GetCell(4).SetBackgroundColor("fb0a2a")
	test.row.GetCell(5).SetBackgroundColor("fb0a2a")
	test.row.GetCell(6).SetBackgroundColor("fb0a2a")
	test.row.GetCell(7).SetBackgroundColor("fb0a2a")
	test.row.GetCell(8).SetBackgroundColor("fb0a2a")
	test.row.GetCell(9).SetBackgroundColor("fb0a2a")
	test.row.GetCell(10).SetBackgroundColor("fb0a2a")
	test.row.GetCell(11).SetBackgroundColor("fb0a2a")
	test.row.GetCell(12).SetBackgroundColor("fb0a2a")
	test.row.GetCell(13).SetBackgroundColor("fb0a2a")
	test.row.GetCell(14).SetBackgroundColor("fb0a2a")
	test.row.GetCell(15).SetBackgroundColor("fb0a2a")
	test.row.GetCell(16).SetBackgroundColor("fb0a2a")
	test.row.GetCell(17).SetBackgroundColor("fb0a2a")
	return test
}
<commit_msg>corresponding to excl. (#2)<commit_after>package excase

import (
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/loadoff/excl"
)

type exStyles struct {
	style1    *excl.Style
	style1New *excl.Style
	style2    *excl.Style
	style2New *excl.Style
	style3    *excl.Style
	style3New *excl.Style
	style4    *excl.Style
	style4New *excl.Style
	style5    *excl.Style
	style5New *excl.Style
	style6    *excl.Style
	style6New *excl.Style
	style7    *excl.Style
	style8    *excl.Style
	style9    *excl.Style
	style10   *excl.Style
	style11   *excl.Style
	style12   *excl.Style
	style13   *excl.Style
	style14   *excl.Style
	style15   *excl.Style
	style16   *excl.Style
	style17   *excl.Style
}

// ExCase テストケース
type ExCase struct {
	FilePath string
	caseBook *excl.Workbook
	dir      string
	styles   *exStyles
	sections []*ExSection
}

// ExSection セクション情報
type ExSection struct {
	testCount   int
	largeCount  int
	middleCount int
	smallCount  int
	styles      *exStyles
	caseSheet   *excl.Sheet
	name        string
	large       string
	middle      string
	small       string
}

// ExTest テストの出力行
type ExTest struct {
	row *excl.Row
}

// InitExCase Excelテストケースを作成する
func InitExCase() *ExCase {
	var err error
	ex := &ExCase{}
	ex.FilePath = strings.Replace(time.Now().Format("20060102030405"), ".", "", 1) + ".xlsx"

	if ex.caseBook, err = excl.Create(); err != nil {
		fmt.Println(err.Error())
		return nil
	}
	// スタイル作成
	style := &excl.Style{Wrap: 1, Vertical: "top"}
	border := excl.Border{Left: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	style.Wrap = 1
	ex.styles = &exStyles{}
	ex.styles.style1 = style
	ex.styles.style3 = style
	ex.styles.style5 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "hair"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	style.Wrap = 1
	ex.styles.style2 = style
	ex.styles.style4 = style
	ex.styles.style6 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "thin"}, Top: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	style.Wrap = 1
	ex.styles.style1New = style
	ex.styles.style3New = style
	ex.styles.style5New = style
	ex.styles.style7 = style
	ex.styles.style9 = style
	ex.styles.style10 = style
	ex.styles.style11 = style
	ex.styles.style12 = style
	ex.styles.style13 = style
	ex.styles.style14 = style
	ex.styles.style15 = style
	ex.styles.style16 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "thin"}, Top: &excl.BorderSetting{Style: "thin"}, Right: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	ex.styles.style17 = style

	style = &excl.Style{Wrap: 1, Vertical: "top"}
	border = excl.Border{Left: &excl.BorderSetting{Style: "hair"}, Top: &excl.BorderSetting{Style: "thin"}}
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	ex.styles.style2New = style
	ex.styles.style4New = style
	ex.styles.style6New = style
	ex.styles.style8 = style
	return ex
}

// OpenSection 新しいシートにテストを出力する
func (ex *ExCase) OpenSection(name string) *ExSection {
	for _, sec := range ex.sections {
		if sec.name == name {
			return sec
		}
	}
	sec := &ExSection{name: name, styles: ex.styles}
	sec.caseSheet, _ = ex.caseBook.OpenSheet(name)
	sec.caseSheet.ShowGridlines(false)
	caseRow := sec.caseSheet.GetRow(4)
	borderSetting := &excl.BorderSetting{Style: "thin"}
	border := excl.Border{Left: borderSetting, Right: borderSetting, Top: borderSetting, Bottom: borderSetting}
	font := excl.Font{Color: "FFFFFF"}
	style := &excl.Style{}
	style.FontID = ex.caseBook.Styles.SetFont(font)
	style.FillID = ex.caseBook.Styles.SetBackgroundColor("361e6d")
	style.BorderID = ex.caseBook.Styles.SetBorder(border)
	caseRow.SetString("No.", 1).SetStyle(style)
	caseRow.SetString("大項目名", 2).SetStyle(style)
	caseRow.SetString("No.", 3).SetStyle(style)
	caseRow.SetString("中項目名", 4).SetStyle(style)
	caseRow.SetString("No.", 5).SetStyle(style)
	caseRow.SetString("小項目名", 6).SetStyle(style)
	caseRow.SetString("No.", 7).SetStyle(style)
	caseRow.SetString("実施内容", 8).SetStyle(style)
	caseRow.SetString("合格条件", 9).SetStyle(style)
	caseRow.SetString("実施日", 10).SetStyle(style)
	caseRow.SetString("実施者", 11).SetStyle(style)
	caseRow.SetString("結果", 12).SetStyle(style)
	caseRow.SetString("補足", 13).SetStyle(style)
	caseRow.SetString("エビデンス", 14).SetStyle(style)
	caseRow.SetString("検証日", 15).SetStyle(style)
	caseRow.SetString("検証者", 16).SetStyle(style)
	caseRow.SetString("結果", 17).SetStyle(style)
	ex.sections = append(ex.sections, sec)
	return sec
}

// CloseSection セクションを閉じる
func (ex *ExSection) CloseSection() {
	if ex.caseSheet == nil {
		return
	}
	caseRow := ex.caseSheet.GetRow(ex.testCount + 5)
	border := excl.Border{Top: &excl.BorderSetting{Style: "thin"}}
	style := &excl.Style{}
	style.BorderID = ex.caseSheet.Styles.SetBorder(border)
	caseRow.GetCell(1).SetStyle(style)
	caseRow.GetCell(2).SetStyle(style)
	caseRow.GetCell(3).SetStyle(style)
	caseRow.GetCell(4).SetStyle(style)
	caseRow.GetCell(5).SetStyle(style)
	caseRow.GetCell(6).SetStyle(style)
	caseRow.GetCell(7).SetStyle(style)
	caseRow.GetCell(8).SetStyle(style)
	caseRow.GetCell(9).SetStyle(style)
	caseRow.GetCell(10).SetStyle(style)
	caseRow.GetCell(11).SetStyle(style)
	caseRow.GetCell(12).SetStyle(style)
	caseRow.GetCell(13).SetStyle(style)
	caseRow.GetCell(14).SetStyle(style)
	caseRow.GetCell(15).SetStyle(style)
	caseRow.GetCell(16).SetStyle(style)
	caseRow.GetCell(17).SetStyle(style)
	ex.caseSheet.Close()
	ex.caseSheet = nil
}

// Close 閉じる
func (ex *ExCase) Close() {
	for _, sec := range ex.sections {
		sec.CloseSection()
	}
	ex.caseBook.Save(ex.FilePath)
}

// Large 大項目をセット
func (ex *ExSection) Large(name string) *ExSection {
	ex.largeCount++
	ex.middleCount = 0
	ex.smallCount = 0
	ex.large = name
	return ex
}

// Middle 中項目をセット
func (ex *ExSection) Middle(name string) *ExSection {
	ex.smallCount = 0
	ex.middleCount++
	ex.middle = name
	/*	*/
	return ex
}

// Small 小項目をセット
func (ex *ExSection) Small(name string) *ExSection {
	ex.smallCount++
	ex.small = name
	return ex
}

// Test テストの内容と合格条件をセットする
func (ex *ExSection) Test(content string, pass string) *ExTest {
	ex.testCount++
	test := &ExTest{}
	test.row = ex.caseSheet.GetRow(ex.testCount + 4)

	test.row.GetCell(1).SetStyle(ex.styles.style1)
	test.row.GetCell(2).SetStyle(ex.styles.style2)
	test.row.GetCell(3).SetStyle(ex.styles.style3)
	test.row.GetCell(4).SetStyle(ex.styles.style4)
	test.row.GetCell(5).SetStyle(ex.styles.style5)
	test.row.GetCell(6).SetStyle(ex.styles.style6)
	test.row.GetCell(7).SetStyle(ex.styles.style7)
	test.row.GetCell(8).SetStyle(ex.styles.style8)
	test.row.GetCell(9).SetStyle(ex.styles.style9)
	test.row.GetCell(10).SetStyle(ex.styles.style10)
	test.row.GetCell(11).SetStyle(ex.styles.style11)
	test.row.GetCell(12).SetStyle(ex.styles.style12)
	test.row.GetCell(13).SetStyle(ex.styles.style13)
	test.row.GetCell(14).SetStyle(ex.styles.style14)
	test.row.GetCell(15).SetStyle(ex.styles.style15)
	test.row.GetCell(16).SetStyle(ex.styles.style16)
	test.row.GetCell(17).SetStyle(ex.styles.style17)

	// 大項目処理
	if ex.large != "" {
		test.row.SetNumber(strconv.Itoa(ex.largeCount), 1).SetStyle(ex.styles.style1New)
		test.row.SetString(ex.large, 2).SetStyle(ex.styles.style2New)
		test.row.GetCell(3).SetStyle(ex.styles.style3New)
		test.row.GetCell(4).SetStyle(ex.styles.style4New)
		test.row.GetCell(5).SetStyle(ex.styles.style5New)
		test.row.GetCell(6).SetStyle(ex.styles.style6New)
		ex.large = ""
	}
	// 中項目処理
	if ex.middle != "" {
		test.row.SetNumber(strconv.Itoa(ex.middleCount), 3).SetStyle(ex.styles.style3New)
		test.row.SetString(ex.middle, 4).SetStyle(ex.styles.style4New)
		test.row.GetCell(5).SetStyle(ex.styles.style5New)
		test.row.GetCell(6).SetStyle(ex.styles.style6New)
		ex.middle = ""
	}
	// 小項目処理
	if ex.small != "" {
		test.row.SetNumber(strconv.Itoa(ex.smallCount), 5).SetStyle(ex.styles.style5New)
		test.row.SetString(ex.small, 6).SetStyle(ex.styles.style6New)
		ex.small = ""
	}
	test.row.SetNumber(strconv.Itoa(ex.testCount), 7)
	test.row.SetString(content, 8)
	test.row.SetString(pass, 9)
	return test
}

// Passed 合格をセット
func (test *ExTest) Passed() *ExTest {
	test.row.SetString(time.Now().Format("01/02"), 10)
	test.row.SetString("合格", 12)
	return test
}

// Failed 不合格をセット
func (test *ExTest) Failed() *ExTest {
	test.row.SetString(time.Now().Format("01/02"), 10)
	test.row.SetString("不合格", 12)

	test.row.GetCell(1).SetBackgroundColor("fb0a2a")
	test.row.GetCell(2).SetBackgroundColor("fb0a2a")
	test.row.GetCell(3).SetBackgroundColor("fb0a2a")
	test.row.GetCell(4).SetBackgroundColor("fb0a2a")
	test.row.GetCell(5).SetBackgroundColor("fb0a2a")
	test.row.GetCell(6).SetBackgroundColor("fb0a2a")
	test.row.GetCell(7).SetBackgroundColor("fb0a2a")
	test.row.GetCell(8).SetBackgroundColor("fb0a2a")
	test.row.GetCell(9).SetBackgroundColor("fb0a2a")
	test.row.GetCell(10).SetBackgroundColor("fb0a2a")
	test.row.GetCell(11).SetBackgroundColor("fb0a2a")
	test.row.GetCell(12).SetBackgroundColor("fb0a2a")
	test.row.GetCell(13).SetBackgroundColor("fb0a2a")
	test.row.GetCell(14).SetBackgroundColor("fb0a2a")
	test.row.GetCell(15).SetBackgroundColor("fb0a2a")
	test.row.GetCell(16).SetBackgroundColor("fb0a2a")
	test.row.GetCell(17).SetBackgroundColor("fb0a2a")
	return test
}
<|endoftext|>