<commit_before>// Package memdata implements data over a set of in memory maps.
package memdata

import (
	"sort"

	"hawx.me/code/riviera/river/data"
)

type database struct {
	buckets map[string]data.Bucket
}

// Open a new in memory database.
func Open() data.Database {
	return &database{buckets: map[string]data.Bucket{}}
}

func (db *database) Bucket(name []byte) (data.Bucket, error) {
	if b, ok := db.buckets[string(name)]; ok {
		return b, nil
	}

	b := &bucket{kv: map[string][]byte{}}
	db.buckets[string(name)] = b
	return b, nil
}

func (db *database) Close() error {
	return nil
}

type bucket struct {
	kv map[string][]byte
}

func (b *bucket) View(t func(data.ReadTx) error) error {
	return t(tx{b})
}

func (b *bucket) Update(t func(data.Tx) error) error {
	return t(tx{b})
}

type tx struct {
	b *bucket
}

func (x tx) Get(key []byte) []byte {
	return x.b.kv[string(key)]
}

func (x tx) Put(key, value []byte) error {
	x.b.kv[string(key)] = value
	return nil
}

func (x tx) Delete(key []byte) error {
	delete(x.b.kv, string(key))
	return nil
}

func (x tx) After(start []byte) [][]byte {
	var ks []string
	for k := range x.b.kv {
		ks = append(ks, k)
	}

	sort.Strings(ks)

	i := sort.Search(len(ks), func(i int) bool {
		return ks[i] >= string(start)
	})

	ks = ks[i:]

	vs := make([][]byte, len(ks))
	for i, k := range ks {
		vs[i] = x.b.kv[k]
	}

	return vs
}

func (x tx) KeysBefore(last []byte) [][]byte {
	var ks []string
	for k := range x.b.kv {
		ks = append(ks, k)
	}

	sort.Strings(ks)

	i := sort.Search(len(ks), func(i int) bool {
		return ks[i] >= string(last)
	})

	if i < len(ks) {
		ks = ks[:i]
	}

	r := make([][]byte, len(ks))
	for i, k := range ks {
		r[i] = []byte(k)
	}

	return r
}

func (x tx) All() [][]byte {
	var ks []string
	for k := range x.b.kv {
		ks = append(ks, k)
	}

	sort.Strings(ks)

	vs := make([][]byte, len(ks))
	for i, k := range ks {
		vs[i] = x.b.kv[k]
	}

	return vs
}
<commit_msg>Prevent races in memdata<commit_after>// Package memdata implements data over a set of in memory maps.
package memdata

import (
	"sort"
	"sync"

	"hawx.me/code/riviera/river/data"
)

type database struct {
	buckets map[string]data.Bucket
	mu      sync.Mutex
}

// Open a new in memory database.
func Open() data.Database {
	return &database{buckets: map[string]data.Bucket{}}
}

func (db *database) Bucket(name []byte) (data.Bucket, error) {
	db.mu.Lock()

	if b, ok := db.buckets[string(name)]; ok {
		return b, nil
	}

	b := &bucket{kv: map[string][]byte{}}
	db.buckets[string(name)] = b
	db.mu.Unlock()

	return b, nil
}

func (db *database) Close() error {
	return nil
}

type bucket struct {
	kv map[string][]byte
	mu sync.RWMutex
}

func (b *bucket) View(t func(data.ReadTx) error) error {
	b.mu.RLock()
	defer b.mu.RUnlock()

	return t(tx{b})
}

func (b *bucket) Update(t func(data.Tx) error) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	return t(tx{b})
}

type tx struct {
	b *bucket
}

func (x tx) Get(key []byte) []byte {
	return x.b.kv[string(key)]
}

func (x tx) Put(key, value []byte) error {
	x.b.kv[string(key)] = value
	return nil
}

func (x tx) Delete(key []byte) error {
	delete(x.b.kv, string(key))
	return nil
}

func (x tx) After(start []byte) [][]byte {
	var ks []string
	for k := range x.b.kv {
		ks = append(ks, k)
	}

	sort.Strings(ks)

	i := sort.Search(len(ks), func(i int) bool {
		return ks[i] >= string(start)
	})

	ks = ks[i:]

	vs := make([][]byte, len(ks))
	for i, k := range ks {
		vs[i] = x.b.kv[k]
	}

	return vs
}

func (x tx) KeysBefore(last []byte) [][]byte {
	var ks []string
	for k := range x.b.kv {
		ks = append(ks, k)
	}

	sort.Strings(ks)

	i := sort.Search(len(ks), func(i int) bool {
		return ks[i] >= string(last)
	})

	if i < len(ks) {
		ks = ks[:i]
	}

	r := make([][]byte, len(ks))
	for i, k := range ks {
		r[i] = []byte(k)
	}

	return r
}

func (x tx) All() [][]byte {
	var ks []string
	for k := range x.b.kv {
		ks = append(ks, k)
	}

	sort.Strings(ks)

	vs := make([][]byte, len(ks))
	for i, k := range ks {
		vs[i] = x.b.kv[k]
	}

	return vs
}
<|endoftext|>


<commit_before>// Copyright 2014 CoreOS, Inc.
//
// 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 registry

import (
	"errors"
	"fmt"
	"path"
	"sort"

	etcd "github.com/coreos/fleet/Godeps/_workspace/src/github.com/coreos/etcd/client"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/log"
	"github.com/coreos/fleet/unit"
)

const (
	jobPrefix = "job"
)

// Schedule returns all ScheduledUnits known by fleet, ordered by name
func (r *EtcdRegistry) Schedule() ([]job.ScheduledUnit, error) {
	key := r.prefixed(jobPrefix)
	opts := &etcd.GetOptions{
		Sort:      true,
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	heartbeats := make(map[string]string)
	uMap := make(map[string]*job.ScheduledUnit)

	for _, dir := range res.Node.Nodes {
		_, name := path.Split(dir.Key)
		u := &job.ScheduledUnit{
			Name:            name,
			TargetMachineID: dirToTargetMachineID(dir),
		}
		heartbeats[name] = dirToHeartbeat(dir)
		uMap[name] = u
	}

	states, err := r.statesByMUSKey()
	if err != nil {
		return nil, err
	}

	var sortable sort.StringSlice

	// Determine the JobState of each ScheduledUnit
	for name, su := range uMap {
		sortable = append(sortable, name)
		key := MUSKey{
			machID: su.TargetMachineID,
			name:   name,
		}
		us := states[key]
		js := determineJobState(heartbeats[name], su.TargetMachineID, us)
		su.State = &js
	}
	sortable.Sort()

	units := make([]job.ScheduledUnit, 0, len(sortable))
	for _, name := range sortable {
		units = append(units, *uMap[name])
	}
	return units, nil
}

// Units lists all Units stored in the Registry, ordered by name. This includes both global and non-global units.
func (r *EtcdRegistry) Units() ([]job.Unit, error) {
	key := r.prefixed(jobPrefix)
	opts := &etcd.GetOptions{
		// We need Job Units to be sorted
		Sort:      true,
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	// Fetch all units by hash recursively to avoid sending N requests to Etcd.
	hashToUnit, err := r.getAllUnitsHashMap()
	if err != nil {
		log.Errorf("failed fetching all Units from etcd: %v", err)
		return nil, err
	}
	unitHashLookupFunc := func(hash unit.Hash) *unit.UnitFile {
		stringHash := hash.String()
		unit, ok := hashToUnit[stringHash]
		if !ok {
			log.Errorf("did not find Unit %v in list of all units", stringHash)
			return nil
		}
		return unit
	}

	units := make([]job.Unit, 0)
	for _, dir := range res.Node.Nodes {
		u, err := r.dirToUnit(dir, unitHashLookupFunc)
		if err != nil {
			log.Errorf("Failed to parse Unit from etcd: %v", err)
			continue
		}
		if u == nil {
			continue
		}

		units = append(units, *u)
	}

	return units, nil
}

// Unit retrieves the Unit by the given name from the Registry. Returns nil if
// no such Unit exists, and any error encountered.
func (r *EtcdRegistry) Unit(name string) (*job.Unit, error) {
	key := r.prefixed(jobPrefix, name)
	opts := &etcd.GetOptions{
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	return r.dirToUnit(res.Node, r.getUnitByHash)
}

// dirToUnit takes a Node containing a Job's constituent objects (in child
// nodes) and returns a *job.Unit, or any error encountered
func (r *EtcdRegistry) dirToUnit(dir *etcd.Node, unitHashLookupFunc func(unit.Hash) *unit.UnitFile) (*job.Unit, error) {
	objKey := path.Join(dir.Key, "object")
	var objNode *etcd.Node
	for _, node := range dir.Nodes {
		node := node
		if node.Key == objKey {
			objNode = node
		}
	}
	if objNode == nil {
		return nil, nil
	}
	u, err := r.getUnitFromObjectNode(objNode, unitHashLookupFunc)
	if err != nil {
		return nil, err
	}
	if u == nil {
		return nil, fmt.Errorf("unable to parse Unit in Registry at key %s", objKey)
	}
	if tgtstate := dirToTargetState(dir); tgtstate != "" {
		ts, err := job.ParseJobState(tgtstate)
		if err != nil {
			return nil, fmt.Errorf("failed to parse Unit(%s) target-state: %v", u.Name, err)
		}
		u.TargetState = ts
	}

	return u, nil
}

// ScheduledUnit retrieves the ScheduledUnit by the given name from the Registry.
// Returns nil if no such ScheduledUnit exists, and any error encountered.
func (r *EtcdRegistry) ScheduledUnit(name string) (*job.ScheduledUnit, error) {
	key := r.prefixed(jobPrefix, name)
	opts := &etcd.GetOptions{
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	su := job.ScheduledUnit{
		Name:            name,
		TargetMachineID: dirToTargetMachineID(res.Node),
	}

	var us *unit.UnitState
	if len(su.TargetMachineID) > 0 {
		us, err = r.getUnitState(name, su.TargetMachineID)
		if err != nil {
			return nil, err
		}
	}

	js := determineJobState(dirToHeartbeat(res.Node), su.TargetMachineID, us)
	su.State = &js

	return &su, nil
}

func (r *EtcdRegistry) UnscheduleUnit(name, machID string) error {
	key := r.jobTargetAgentPath(name)
	opts := &etcd.DeleteOptions{
		PrevValue: machID,
	}
	_, err := r.kAPI.Delete(r.ctx(), key, opts)
	if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
		err = nil
	}

	return err
}

// getValueInDir takes a *etcd.Node containing a job, and returns the value of
// the given key within that directory (i.e. child node) as a string, or an
// empty string if the child node does not exist
func getValueInDir(dir *etcd.Node, key string) (value string) {
	valPath := path.Join(dir.Key, key)
	for _, node := range dir.Nodes {
		if node.Key == valPath {
			value = node.Value
			break
		}
	}
	return
}

func dirToTargetMachineID(dir *etcd.Node) (tgtMID string) {
	return getValueInDir(dir, "target")
}

func dirToTargetState(dir *etcd.Node) (tgtState string) {
	return getValueInDir(dir, "target-state")
}

func dirToHeartbeat(dir *etcd.Node) (heartbeat string) {
	return getValueInDir(dir, "job-state")
}

// getUnitFromObject takes a *etcd.Node containing a Unit's jobModel, and
// instantiates and returns a representative *job.Unit, transitively fetching the
// associated UnitFile as necessary
func (r *EtcdRegistry) getUnitFromObjectNode(node *etcd.Node, unitHashLookupFunc func(unit.Hash) *unit.UnitFile) (*job.Unit, error) {
	var err error
	var jm jobModel
	if err = unmarshal(node.Value, &jm); err != nil {
		return nil, err
	}

	var unit *unit.UnitFile

	unit = unitHashLookupFunc(jm.UnitHash)
	if unit == nil {
		log.Warningf("No Unit found in Registry for Job(%s)", jm.Name)
		return nil, nil
	}

	ju := &job.Unit{
		Name: jm.Name,
		Unit: *unit,
	}
	return ju, nil

}

// jobModel is used for serializing and deserializing Jobs stored in the Registry
type jobModel struct {
	Name     string
	UnitHash unit.Hash
}

// DestroyUnit removes a Job object from the repository. It does not yet remove underlying
// UnitFiles from the repository.
func (r *EtcdRegistry) DestroyUnit(name string) error {
	key := r.prefixed(jobPrefix, name)
	opts := &etcd.DeleteOptions{
		Recursive: true,
	}
	_, err := r.kAPI.Delete(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = errors.New("job does not exist")
		}

		return err
	}

	// TODO(jonboulle): add unit reference counting and actually destroying Units
	return nil
}

// CreateUnit attempts to store a Unit and its associated unit file in the registry
func (r *EtcdRegistry) CreateUnit(u *job.Unit) (err error) {
	if err := r.storeOrGetUnitFile(u.Unit); err != nil {
		return err
	}

	jm := jobModel{
		Name:     u.Name,
		UnitHash: u.Unit.Hash(),
	}
	val, err := marshal(jm)
	if err != nil {
		return
	}

	opts := &etcd.SetOptions{
		PrevExist: etcd.PrevNoExist,
	}
	key := r.prefixed(jobPrefix, u.Name, "object")
	_, err = r.kAPI.Set(r.ctx(), key, val, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeNodeExist) {
			err = errors.New("job already exists")
		}
		return
	}

	return r.SetUnitTargetState(u.Name, u.TargetState)
}

func (r *EtcdRegistry) SetUnitTargetState(name string, state job.JobState) error {
	key := r.jobTargetStatePath(name)
	_, err := r.kAPI.Set(r.ctx(), key, string(state), nil)
	return err
}

func (r *EtcdRegistry) ScheduleUnit(name string, machID string) error {
	key := r.jobTargetAgentPath(name)
	opts := &etcd.SetOptions{
		PrevExist: etcd.PrevNoExist,
	}
	_, err := r.kAPI.Set(r.ctx(), key, machID, opts)
	return err
}

func (r *EtcdRegistry) jobTargetAgentPath(jobName string) string {
	return r.prefixed(jobPrefix, jobName, "target")
}

func (r *EtcdRegistry) jobTargetStatePath(jobName string) string {
	return r.prefixed(jobPrefix, jobName, "target-state")
}
<commit_msg>registry: instruct etcd driver to allow updating job object keys<commit_after>// Copyright 2014 CoreOS, Inc.
//
// 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 registry

import (
	"errors"
	"fmt"
	"path"
	"sort"

	etcd "github.com/coreos/fleet/Godeps/_workspace/src/github.com/coreos/etcd/client"

	"github.com/coreos/fleet/job"
	"github.com/coreos/fleet/log"
	"github.com/coreos/fleet/unit"
)

const (
	jobPrefix = "job"
)

// Schedule returns all ScheduledUnits known by fleet, ordered by name
func (r *EtcdRegistry) Schedule() ([]job.ScheduledUnit, error) {
	key := r.prefixed(jobPrefix)
	opts := &etcd.GetOptions{
		Sort:      true,
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	heartbeats := make(map[string]string)
	uMap := make(map[string]*job.ScheduledUnit)

	for _, dir := range res.Node.Nodes {
		_, name := path.Split(dir.Key)
		u := &job.ScheduledUnit{
			Name:            name,
			TargetMachineID: dirToTargetMachineID(dir),
		}
		heartbeats[name] = dirToHeartbeat(dir)
		uMap[name] = u
	}

	states, err := r.statesByMUSKey()
	if err != nil {
		return nil, err
	}

	var sortable sort.StringSlice

	// Determine the JobState of each ScheduledUnit
	for name, su := range uMap {
		sortable = append(sortable, name)
		key := MUSKey{
			machID: su.TargetMachineID,
			name:   name,
		}
		us := states[key]
		js := determineJobState(heartbeats[name], su.TargetMachineID, us)
		su.State = &js
	}
	sortable.Sort()

	units := make([]job.ScheduledUnit, 0, len(sortable))
	for _, name := range sortable {
		units = append(units, *uMap[name])
	}
	return units, nil
}

// Units lists all Units stored in the Registry, ordered by name. This includes both global and non-global units.
func (r *EtcdRegistry) Units() ([]job.Unit, error) {
	key := r.prefixed(jobPrefix)
	opts := &etcd.GetOptions{
		// We need Job Units to be sorted
		Sort:      true,
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	// Fetch all units by hash recursively to avoid sending N requests to Etcd.
	hashToUnit, err := r.getAllUnitsHashMap()
	if err != nil {
		log.Errorf("failed fetching all Units from etcd: %v", err)
		return nil, err
	}
	unitHashLookupFunc := func(hash unit.Hash) *unit.UnitFile {
		stringHash := hash.String()
		unit, ok := hashToUnit[stringHash]
		if !ok {
			log.Errorf("did not find Unit %v in list of all units", stringHash)
			return nil
		}
		return unit
	}

	units := make([]job.Unit, 0)
	for _, dir := range res.Node.Nodes {
		u, err := r.dirToUnit(dir, unitHashLookupFunc)
		if err != nil {
			log.Errorf("Failed to parse Unit from etcd: %v", err)
			continue
		}
		if u == nil {
			continue
		}

		units = append(units, *u)
	}

	return units, nil
}

// Unit retrieves the Unit by the given name from the Registry. Returns nil if
// no such Unit exists, and any error encountered.
func (r *EtcdRegistry) Unit(name string) (*job.Unit, error) {
	key := r.prefixed(jobPrefix, name)
	opts := &etcd.GetOptions{
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	return r.dirToUnit(res.Node, r.getUnitByHash)
}

// dirToUnit takes a Node containing a Job's constituent objects (in child
// nodes) and returns a *job.Unit, or any error encountered
func (r *EtcdRegistry) dirToUnit(dir *etcd.Node, unitHashLookupFunc func(unit.Hash) *unit.UnitFile) (*job.Unit, error) {
	objKey := path.Join(dir.Key, "object")
	var objNode *etcd.Node
	for _, node := range dir.Nodes {
		node := node
		if node.Key == objKey {
			objNode = node
		}
	}
	if objNode == nil {
		return nil, nil
	}
	u, err := r.getUnitFromObjectNode(objNode, unitHashLookupFunc)
	if err != nil {
		return nil, err
	}
	if u == nil {
		return nil, fmt.Errorf("unable to parse Unit in Registry at key %s", objKey)
	}
	if tgtstate := dirToTargetState(dir); tgtstate != "" {
		ts, err := job.ParseJobState(tgtstate)
		if err != nil {
			return nil, fmt.Errorf("failed to parse Unit(%s) target-state: %v", u.Name, err)
		}
		u.TargetState = ts
	}

	return u, nil
}

// ScheduledUnit retrieves the ScheduledUnit by the given name from the Registry.
// Returns nil if no such ScheduledUnit exists, and any error encountered.
func (r *EtcdRegistry) ScheduledUnit(name string) (*job.ScheduledUnit, error) {
	key := r.prefixed(jobPrefix, name)
	opts := &etcd.GetOptions{
		Recursive: true,
	}
	res, err := r.kAPI.Get(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = nil
		}
		return nil, err
	}

	su := job.ScheduledUnit{
		Name:            name,
		TargetMachineID: dirToTargetMachineID(res.Node),
	}

	var us *unit.UnitState
	if len(su.TargetMachineID) > 0 {
		us, err = r.getUnitState(name, su.TargetMachineID)
		if err != nil {
			return nil, err
		}
	}

	js := determineJobState(dirToHeartbeat(res.Node), su.TargetMachineID, us)
	su.State = &js

	return &su, nil
}

func (r *EtcdRegistry) UnscheduleUnit(name, machID string) error {
	key := r.jobTargetAgentPath(name)
	opts := &etcd.DeleteOptions{
		PrevValue: machID,
	}
	_, err := r.kAPI.Delete(r.ctx(), key, opts)
	if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
		err = nil
	}

	return err
}

// getValueInDir takes a *etcd.Node containing a job, and returns the value of
// the given key within that directory (i.e. child node) as a string, or an
// empty string if the child node does not exist
func getValueInDir(dir *etcd.Node, key string) (value string) {
	valPath := path.Join(dir.Key, key)
	for _, node := range dir.Nodes {
		if node.Key == valPath {
			value = node.Value
			break
		}
	}
	return
}

func dirToTargetMachineID(dir *etcd.Node) (tgtMID string) {
	return getValueInDir(dir, "target")
}

func dirToTargetState(dir *etcd.Node) (tgtState string) {
	return getValueInDir(dir, "target-state")
}

func dirToHeartbeat(dir *etcd.Node) (heartbeat string) {
	return getValueInDir(dir, "job-state")
}

// getUnitFromObject takes a *etcd.Node containing a Unit's jobModel, and
// instantiates and returns a representative *job.Unit, transitively fetching the
// associated UnitFile as necessary
func (r *EtcdRegistry) getUnitFromObjectNode(node *etcd.Node, unitHashLookupFunc func(unit.Hash) *unit.UnitFile) (*job.Unit, error) {
	var err error
	var jm jobModel
	if err = unmarshal(node.Value, &jm); err != nil {
		return nil, err
	}

	var unit *unit.UnitFile

	unit = unitHashLookupFunc(jm.UnitHash)
	if unit == nil {
		log.Warningf("No Unit found in Registry for Job(%s)", jm.Name)
		return nil, nil
	}

	ju := &job.Unit{
		Name: jm.Name,
		Unit: *unit,
	}
	return ju, nil

}

// jobModel is used for serializing and deserializing Jobs stored in the Registry
type jobModel struct {
	Name     string
	UnitHash unit.Hash
}

// DestroyUnit removes a Job object from the repository. It does not yet remove underlying
// UnitFiles from the repository.
func (r *EtcdRegistry) DestroyUnit(name string) error {
	key := r.prefixed(jobPrefix, name)
	opts := &etcd.DeleteOptions{
		Recursive: true,
	}
	_, err := r.kAPI.Delete(r.ctx(), key, opts)
	if err != nil {
		if isEtcdError(err, etcd.ErrorCodeKeyNotFound) {
			err = errors.New("job does not exist")
		}

		return err
	}

	// TODO(jonboulle): add unit reference counting and actually destroying Units
	return nil
}

// CreateUnit attempts to store a Unit and its associated unit file in the registry
func (r *EtcdRegistry) CreateUnit(u *job.Unit) (err error) {
	if err := r.storeOrGetUnitFile(u.Unit); err != nil {
		return err
	}

	jm := jobModel{
		Name:     u.Name,
		UnitHash: u.Unit.Hash(),
	}
	val, err := marshal(jm)
	if err != nil {
		return
	}

	opts := &etcd.SetOptions{
		// Since we support replacing units, just ignore previous
		// job keys if they exist, this allows us to update the
		// job object key with a new unit.
		PrevExist: etcd.PrevIgnore,
	}
	key := r.prefixed(jobPrefix, u.Name, "object")
	_, err = r.kAPI.Set(r.ctx(), key, val, opts)
	if err != nil {
		return
	}

	return r.SetUnitTargetState(u.Name, u.TargetState)
}

func (r *EtcdRegistry) SetUnitTargetState(name string, state job.JobState) error {
	key := r.jobTargetStatePath(name)
	_, err := r.kAPI.Set(r.ctx(), key, string(state), nil)
	return err
}

func (r *EtcdRegistry) ScheduleUnit(name string, machID string) error {
	key := r.jobTargetAgentPath(name)
	opts := &etcd.SetOptions{
		PrevExist: etcd.PrevNoExist,
	}
	_, err := r.kAPI.Set(r.ctx(), key, machID, opts)
	return err
}

func (r *EtcdRegistry) jobTargetAgentPath(jobName string) string {
	return r.prefixed(jobPrefix, jobName, "target")
}

func (r *EtcdRegistry) jobTargetStatePath(jobName string) string {
	return r.prefixed(jobPrefix, jobName, "target-state")
}
<|endoftext|>


<commit_before>package main

import (
	"koding/kite"
	"koding/kite/regserv/regserv"
	"koding/kite/testkeys"
)

func main() {
	b := &exampleBackend{}
	s := regserv.New(b)
	s.Run()
}

type exampleBackend struct{}

func (b *exampleBackend) Issuer() string     { return "example.com" }
func (b *exampleBackend) KontrolURL() string { return "http://localhost:4000/kontrol" }
func (b *exampleBackend) PublicKey() string  { return testkeys.Public }
func (b *exampleBackend) PrivateKey() string { return testkeys.Private }

func (b *exampleBackend) Authenticate(r *kite.Request) (string, error) {
	return "testuser", nil
}
<commit_msg>fix default kontrol url<commit_after>package main

import (
	"koding/kite"
	"koding/kite/regserv/regserv"
	"koding/kite/testkeys"
)

func main() {
	b := &exampleBackend{}
	s := regserv.New(b)
	s.Run()
}

type exampleBackend struct{}

func (b *exampleBackend) Issuer() string     { return "example.com" }
func (b *exampleBackend) KontrolURL() string { return "ws://localhost:4000/kontrol" }
func (b *exampleBackend) PublicKey() string  { return testkeys.Public }
func (b *exampleBackend) PrivateKey() string { return testkeys.Private }

func (b *exampleBackend) Authenticate(r *kite.Request) (string, error) {
	return "testuser", nil
}
<|endoftext|>


<commit_before>package remote

import (
	//"fmt"
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/yinqiwen/gsnova/common/event"
)

var proxySessionMap map[SessionId]*ProxySession = make(map[SessionId]*ProxySession)
var sessionMutex sync.Mutex

type ConnId struct {
	User      string
	ConnIndex int
	RunId     int64
}

type ConnContext struct {
	ConnId
	IV            uint64
	EncryptMethod int
	Closing       bool
}

func NewConnContext() *ConnContext {
	ctx := new(ConnContext)
	return ctx
}

type SessionId struct {
	ConnId
	Id uint32
}

type ProxySession struct {
	Id            SessionId
	CreateTime    time.Time
	conn          net.Conn
	addr          string
	network       string
	ch            chan event.Event
	closeByClient bool
}

func GetSessionTableSize() int {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	return len(proxySessionMap)
}
func DumpAllSession(wr io.Writer) {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	for _, session := range proxySessionMap {
		session.dump(wr)
	}
}

func getProxySessionByEvent(cid ConnId, ev event.Event) *ProxySession {
	sid := SessionId{cid, ev.GetId()}
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	if p, exist := proxySessionMap[sid]; exist {
		return p
	}
	createIfMissing := false
	switch ev.(type) {
	case *event.TCPOpenEvent:
		createIfMissing = true
	case *event.HTTPRequestEvent:
		createIfMissing = true
	case *event.UDPEvent:
		createIfMissing = true
	}
	if !createIfMissing {
		return nil
	}
	p := new(ProxySession)
	p.Id = sid
	p.CreateTime = time.Now()
	p.ch = make(chan event.Event, 100)
	go p.processEvents()
	proxySessionMap[sid] = p
	return p
}

func destroyProxySession(s *ProxySession) {
	delete(proxySessionMap, s.Id)
	s.ch <- nil
	close(s.ch)
}

func removeProxySession(s *ProxySession) {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	_, exist := proxySessionMap[s.Id]
	if exist {
		destroyProxySession(s)
		//log.Printf("Remove sesion:%d, %d left", s.Id.Id, len(proxySessionMap))
	}
}
func sessionExist(sid SessionId) bool {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	_, exist := proxySessionMap[sid]
	return exist
}

func removeUserSessions(user string, runid int64) {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	for k, s := range proxySessionMap {
		if k.User == user && k.RunId == runid {
			destroyProxySession(s)
		}
	}
}

//just for debug
func (p *ProxySession) dump(wr io.Writer) {
	fmt.Fprintf(wr, "[%d]network=%s,addr=%s,closed=%v\n", p.Id.Id, p.network, p.addr, p.conn == nil)
}

func (p *ProxySession) publish(ev event.Event) {
	ev.SetId(p.Id.Id)
	start := time.Now()
	for !p.closeByClient {
		queue := getEventQueue(p.Id.ConnId, false)
		if nil != queue {
			err := queue.Publish(ev, 10*time.Second)
			if nil != err {
				if !queue.acuired {
					log.Printf("Session[%s:%d] write event error:%v.", p.Id.User, p.Id.Id, err)
					p.forceClose()
				} else {
					continue
				}
			}
			return
		}
		if time.Now().After(start.Add(10 * time.Second)) {
			log.Printf("No avaliable connection to write event")
			p.forceClose()
			break
		}
		time.Sleep(1 * time.Millisecond)
	}
}

func (p *ProxySession) close() error {
	c := p.conn
	if nil != c {
		//log.Printf("Session[%s:%d] close connection to %s", p.Id.User, p.Id.Id, p.addr)
		c.Close()
		p.conn = nil
		p.addr = ""
	}
	return nil
}

func (p *ProxySession) forceClose() {
	p.close()
	removeProxySession(p)
}

func (p *ProxySession) initialClose() {
	if p.network == "tcp" {
		ev := &event.TCPCloseEvent{}
		p.publish(ev)
	}
	p.forceClose()
}

func (p *ProxySession) processEvents() {
	for {
		ev := <-p.ch
		if nil != ev {
			p.handle(ev)
		} else {
			break
		}
	}
}

func (p *ProxySession) open(network, to string) error {
	if p.conn != nil && to == p.addr {
		return nil
	}
	p.close()
	p.network = network
	//log.Printf("Session[%s:%d] open connection to %s.", p.Id.User, p.Id.Id, to)
	c, err := net.DialTimeout(network, to, 5*time.Second)
	if nil != err {
		p.initialClose()
		log.Printf("Failed to connect %s:%s for reason:%v", network, to, err)
		return err
	}
	p.conn = c
	p.addr = to
	go p.readNetwork()
	return nil
}

func (p *ProxySession) write(b []byte) (int, error) {
	if p.conn == nil {
		log.Printf("Session[%s:%d] have no established connection to %s.", p.Id.User, p.Id.Id, p.addr)
		p.initialClose()
		return 0, nil
	}
	n, err := p.conn.Write(b)
	if nil != err {
		p.initialClose()
	}
	return n, err
}

func (p *ProxySession) readNetwork() error {
	for {
		conn := p.conn
		if nil == conn {
			return nil
		}
		conn.SetReadDeadline(time.Now().Add(10 * time.Second))
		b := make([]byte, 8192)
		n, err := conn.Read(b)
		if n > 0 {
			var ev event.Event
			if p.network == "tcp" {
				ev = &event.TCPChunkEvent{Content: b[0:n]}
			} else {
				ev = &event.UDPEvent{Content: b[0:n]}
			}
			p.publish(ev)
		}
		if nil != err {
			p.initialClose()
			return err
		}

	}
	return nil
}
func (p *ProxySession) offer(ev event.Event) {
	p.ch <- ev
}

func (p *ProxySession) handle(ev event.Event) error {
	switch ev.(type) {
	case *event.UDPEvent:
		err := p.open("udp", ev.(*event.UDPEvent).Addr)
		if nil != err {
			return err
		}
		_, err = p.write(ev.(*event.UDPEvent).Content)
		return err
	case *event.TCPOpenEvent:
		return p.open("tcp", ev.(*event.TCPOpenEvent).Addr)
	case *event.TCPCloseEvent:
		p.close()
		removeProxySession(p)
	case *event.TCPChunkEvent:
		p.write(ev.(*event.TCPChunkEvent).Content)
	case *event.HTTPRequestEvent:
		req := ev.(*event.HTTPRequestEvent)
		addr := req.Headers.Get("Host")
		if !strings.Contains(addr, ":") {
			if !strings.EqualFold("Connect", req.Method) {
				addr = addr + ":80"
			} else {
				addr = addr + ":443"
			}
		}
		//log.Printf("Session[%d] %s %s", ev.GetId(), req.Method, req.URL)
		err := p.open("tcp", addr)
		if nil != err {
			return err
		}
		content := req.HTTPEncode()
		_, err = p.write(content)
		return err
	default:
		log.Printf("Invalid event type:%T to process", ev)
	}
	return nil
}

func authConnection(auth *event.AuthEvent, ctx *ConnContext) error {
	//log.Printf("###Recv auth IV = %d, ctx IV = %d", auth.IV, ctx.IV)
	if len(ctx.User) == 0 {
		if !ServerConf.VerifyUser(auth.User) {
			return fmt.Errorf("Auth failed with user:%s", auth.User)
		}
		authedUser := auth.User
		authedUser = authedUser + "@" + auth.Mac
		ctx.User = authedUser
		ctx.ConnIndex = int(auth.Index)
		ctx.IV = auth.IV
		ctx.RunId = auth.RunId
		GetEventQueue(ctx.ConnId, true)
		//log.Printf("###Recv IV = %d", ctx.IV)
		return nil
	} else {
		return fmt.Errorf("Duplicate auth/login event in same connection")
	}
}

func handleEvent(ev event.Event, ctx *ConnContext) (event.Event, error) {
	switch ev.(type) {
	case *event.AuthEvent:
		auth := ev.(*event.AuthEvent)
		err := authConnection(auth, ctx)
		var authres event.NotifyEvent
		authres.SetId(ev.GetId())
		if nil == err {
			authres.Code = event.SuccessAuthed
		} else {
			authres.Code = event.ErrAuthFailed
		}
		return &authres, nil
	case *event.HeartBeatEvent:
		//do nothing
	case *event.ChannelCloseReqEvent:
		ctx.Closing = true
		queue := getEventQueue(ctx.ConnId, false)
		if nil != queue {
			queue.Publish(nil, 1*time.Minute)
		}
	default:
		session := getProxySessionByEvent(ctx.ConnId, ev)
		if nil != session {
			session.offer(ev)
		}
		if _, ok := ev.(*event.TCPCloseEvent); !ok {
			if nil == session {
				log.Printf("No session:%d found for event %T", ev.GetId(), ev)
			}
		} else {
			if nil != session {
				session.closeByClient = true
			}
		}
	}
	return nil, nil
}

func HandleRequestBuffer(reqbuf *bytes.Buffer, ctx *ConnContext) ([]event.Event, error) {
	var ress []event.Event
	for reqbuf.Len() > 0 {
		var ev event.Event
		var err error
		err, ev = event.DecryptEvent(reqbuf, ctx.IV)
		if nil != err {
			if err != event.EBNR {
				log.Printf("Failed to decode event for reason:%v", err)
			}
			return ress, err
		}
		res, err := handleEvent(ev, ctx)
		if nil != res {
			ress = append(ress, res)
		}
		if nil != err {
			return ress, err
		}
	}
	return ress, nil
}
<commit_msg>session leak fix<commit_after>package remote

import (
	//"fmt"
	"bytes"
	"fmt"
	"io"
	"log"
	"net"
	"strings"
	"sync"
	"time"

	"github.com/yinqiwen/gsnova/common/event"
)

var proxySessionMap map[SessionId]*ProxySession = make(map[SessionId]*ProxySession)
var sessionMutex sync.Mutex

type ConnId struct {
	User      string
	ConnIndex int
	RunId     int64
}

type ConnContext struct {
	ConnId
	IV            uint64
	EncryptMethod int
	Closing       bool
}

func NewConnContext() *ConnContext {
	ctx := new(ConnContext)
	return ctx
}

type SessionId struct {
	ConnId
	Id uint32
}

type ProxySession struct {
	Id            SessionId
	CreateTime    time.Time
	conn          net.Conn
	addr          string
	network       string
	ch            chan event.Event
	closeByClient bool
}

func GetSessionTableSize() int {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	return len(proxySessionMap)
}
func DumpAllSession(wr io.Writer) {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	for _, session := range proxySessionMap {
		session.dump(wr)
	}
}

func getProxySessionByEvent(cid ConnId, ev event.Event) *ProxySession {
	sid := SessionId{cid, ev.GetId()}
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	if p, exist := proxySessionMap[sid]; exist {
		return p
	}
	createIfMissing := false
	switch ev.(type) {
	case *event.TCPOpenEvent:
		createIfMissing = true
	case *event.HTTPRequestEvent:
		createIfMissing = true
	case *event.UDPEvent:
		createIfMissing = true
	}
	if !createIfMissing {
		return nil
	}
	p := new(ProxySession)
	p.Id = sid
	p.CreateTime = time.Now()
	p.ch = make(chan event.Event, 100)
	go p.processEvents()
	proxySessionMap[sid] = p
	return p
}

func destroyProxySession(s *ProxySession) {
	delete(proxySessionMap, s.Id)
	s.ch <- nil
	close(s.ch)
}

func removeProxySession(s *ProxySession) {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	_, exist := proxySessionMap[s.Id]
	if exist {
		destroyProxySession(s)
		//log.Printf("Remove sesion:%d, %d left", s.Id.Id, len(proxySessionMap))
	}
}
func sessionExist(sid SessionId) bool {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	_, exist := proxySessionMap[sid]
	return exist
}

func removeUserSessions(user string, runid int64) {
	sessionMutex.Lock()
	defer sessionMutex.Unlock()
	for k, s := range proxySessionMap {
		if k.User == user && k.RunId == runid {
			destroyProxySession(s)
		}
	}
}

//just for debug
func (p *ProxySession) dump(wr io.Writer) {
	fmt.Fprintf(wr, "[%d]network=%s,addr=%s,closed=%v\n", p.Id.Id, p.network, p.addr, p.conn == nil)
}

func (p *ProxySession) publish(ev event.Event) {
	ev.SetId(p.Id.Id)
	start := time.Now()
	for !p.closeByClient {
		queue := getEventQueue(p.Id.ConnId, false)
		if nil != queue {
			err := queue.Publish(ev, 10*time.Second)
			if nil != err {
				if !queue.acuired {
					log.Printf("Session[%s:%d] write event error:%v.", p.Id.User, p.Id.Id, err)
					p.forceClose()
				} else {
					continue
				}
			}
			return
		}
		if time.Now().After(start.Add(10 * time.Second)) {
			log.Printf("No avaliable connection to write event")
			p.forceClose()
			break
		}
		time.Sleep(1 * time.Millisecond)
	}
}

func (p *ProxySession) close() error {
	c := p.conn
	if nil != c {
		//log.Printf("Session[%s:%d] close connection to %s", p.Id.User, p.Id.Id, p.addr)
		c.Close()
		p.conn = nil
		p.addr = ""
	}
	return nil
}

func (p *ProxySession) forceClose() {
	p.close()
	removeProxySession(p)
}

func (p *ProxySession) initialClose() {
	if p.network == "tcp" {
		ev := &event.TCPCloseEvent{}
		p.publish(ev)
	}
	p.forceClose()
}

func (p *ProxySession) processEvents() {
	for {
		select {
		case ev := <-p.ch:
			if nil != ev {
				p.handle(ev)
			} else {
				return
			}
		case <-time.After(time.Second * 30):
			log.Printf("Session[%s:%d] read channel timeout after 30s.", p.Id.User, p.Id.Id)
			p.forceClose()
			return
		}
	}
}

func (p *ProxySession) open(network, to string) error {
	if p.conn != nil && to == p.addr {
		return nil
	}
	p.close()
	p.network = network
	//log.Printf("Session[%s:%d] open connection to %s.", p.Id.User, p.Id.Id, to)
	c, err := net.DialTimeout(network, to, 5*time.Second)
	if nil != err {
		p.initialClose()
		log.Printf("Failed to connect %s:%s for reason:%v", network, to, err)
		return err
	}
	p.conn = c
	p.addr = to
	go p.readNetwork()
	return nil
}

func (p *ProxySession) write(b []byte) (int, error) {
	if p.conn == nil {
		log.Printf("Session[%s:%d] have no established connection to %s.", p.Id.User, p.Id.Id, p.addr)
		p.initialClose()
		return 0, nil
	}
	n, err := p.conn.Write(b)
	if nil != err {
		p.initialClose()
	}
	return n, err
}

func (p *ProxySession) readNetwork() error {
	for {
		conn := p.conn
		if nil == conn {
			return nil
		}
		conn.SetReadDeadline(time.Now().Add(10 * time.Second))
		b := make([]byte, 8192)
		n, err := conn.Read(b)
		if n > 0 {
			var ev event.Event
			if p.network == "tcp" {
				ev = &event.TCPChunkEvent{Content: b[0:n]}
			} else {
				ev = &event.UDPEvent{Content: b[0:n]}
			}
			p.publish(ev)
		}
		if nil != err {
			break
		}

	}
	p.initialClose()
	return nil
}
func (p *ProxySession) offer(ev event.Event) {
	p.ch <- ev
}

func (p *ProxySession) handle(ev event.Event) error {
	switch ev.(type) {
	case *event.UDPEvent:
		err := p.open("udp", ev.(*event.UDPEvent).Addr)
		if nil != err {
			return err
		}
		_, err = p.write(ev.(*event.UDPEvent).Content)
		return err
	case *event.TCPOpenEvent:
		return p.open("tcp", ev.(*event.TCPOpenEvent).Addr)
	case *event.TCPCloseEvent:
		p.close()
		removeProxySession(p)
	case *event.TCPChunkEvent:
		p.write(ev.(*event.TCPChunkEvent).Content)
	case *event.HTTPRequestEvent:
		req := ev.(*event.HTTPRequestEvent)
		addr := req.Headers.Get("Host")
		if !strings.Contains(addr, ":") {
			if !strings.EqualFold("Connect", req.Method) {
				addr = addr + ":80"
			} else {
				addr = addr + ":443"
			}
		}
		//log.Printf("Session[%d] %s %s", ev.GetId(), req.Method, req.URL)
		err := p.open("tcp", addr)
		if nil != err {
			return err
		}
		content := req.HTTPEncode()
		_, err = p.write(content)
		return err
	default:
		log.Printf("Invalid event type:%T to process", ev)
	}
	return nil
}

func authConnection(auth *event.AuthEvent, ctx *ConnContext) error {
	//log.Printf("###Recv auth IV = %d, ctx IV = %d", auth.IV, ctx.IV)
	if len(ctx.User) == 0 {
		if !ServerConf.VerifyUser(auth.User) {
			return fmt.Errorf("Auth failed with user:%s", auth.User)
		}
		authedUser := auth.User
		authedUser = authedUser + "@" + auth.Mac
		ctx.User = authedUser
		ctx.ConnIndex = int(auth.Index)
		ctx.IV = auth.IV
		ctx.RunId = auth.RunId
		GetEventQueue(ctx.ConnId, true)
		//log.Printf("###Recv IV = %d", ctx.IV)
		return nil
	} else {
		return fmt.Errorf("Duplicate auth/login event in same connection")
	}
}

func handleEvent(ev event.Event, ctx *ConnContext) (event.Event, error) {
	switch ev.(type) {
	case *event.AuthEvent:
		auth := ev.(*event.AuthEvent)
		err := authConnection(auth, ctx)
		var authres event.NotifyEvent
		authres.SetId(ev.GetId())
		if nil == err {
			authres.Code = event.SuccessAuthed
		} else {
			authres.Code = event.ErrAuthFailed
		}
		return &authres, nil
	case *event.HeartBeatEvent:
		//do nothing
	case *event.ChannelCloseReqEvent:
		ctx.Closing = true
		queue := getEventQueue(ctx.ConnId, false)
		if nil != queue {
			queue.Publish(nil, 1*time.Minute)
		}
	default:
		session := getProxySessionByEvent(ctx.ConnId, ev)
		if nil != session {
			session.offer(ev)
		}
		if _, ok := ev.(*event.TCPCloseEvent); !ok {
			if nil == session {
				log.Printf("No session:%d found for event %T", ev.GetId(), ev)
			}
		} else {
			if nil != session {
				session.closeByClient = true
			}
		}
	}
	return nil, nil
}

func HandleRequestBuffer(reqbuf *bytes.Buffer, ctx *ConnContext) ([]event.Event, error) {
	var ress []event.Event
	for reqbuf.Len() > 0 {
		var ev event.Event
		var err error
		err, ev = event.DecryptEvent(reqbuf, ctx.IV)
		if nil != err {
			if err != event.EBNR {
				log.Printf("Failed to decode event for reason:%v", err)
			}
			return ress, err
		}
		res, err := handleEvent(ev, ctx)
		if nil != res {
			ress = append(ress, res)
		}
		if nil != err {
			return ress, err
		}
	}
	return ress, nil
}
<|endoftext|>


<commit_before>package gothub

import (
	"fmt"
	"time"
)

type RepositoryOwner struct {
	Login      string `json:"login"`
	Id         int    `json:"id"`
	AvatarUrl  string `json:"avatar_url"`
	GravatarId string `json:"gravatar_id"`
	Url        string `json:"url"`
}

type Repository struct {
	Id               int             `json:"id"`
	Owner            RepositoryOwner `json:"owner"`
	Name             string          `json:"name"`
	FullName         string          `json:"full_name"`
	Description      string          `json:"description"`
	Private          bool            `json:"private"`
	Fork             bool            `json:"fork"`
	Url              string          `json:"url"`
	HtmlUrl          string          `json:"html_url"`
	CloneUrl         string          `json:"clone_url"`
	GitUrl           string          `json:"git_url"`
	SshUrl           string          `json:"ssh_url"`
	SvnUrl           string          `json:"svn_url"`
	MirrorUrl        string          `json:"mirror_url"`
	ArchiveUrl       string          `json:"archive_url"`
	AssigneesUrl     string          `json:"assignees_url"`
	BlobsUrl         string          `json:"blobs_url"`
	CollaboratorsUrl string          `json:"collaborators_url"`
	CommentsUrl      string          `json:"comments_url"`
	CompareUrl       string          `json:"compare_url"`
	ContentsUrl      string          `json:"contentsUrl"`
	ContributorsUrl  string          `json:"contributors_url"`
	Homepage         string          `json:"homepage"`
	Forks            int             `json:"forks"`
	ForksCount       int             `json:"forks_count"`
	Watchers         int             `json:"watchers"`
	WatchersCount    int             `json:"watchers_count"`
	Size             int             `json:"size"`
	MasterBranch     string          `json:"master_branch"`
	OpenIssues       string          `json:"open_issues"`
	PushedAt         time.Time       `json:"pushed_at"`
	CreatedAt        time.Time       `json:"created_at"`
	UpdatedAt        time.Time       `json:"updated_at"`
}

// Get the currently-authenticated user's repositories.
func (g GitHub) Repositories() (repositories []Repository, err error) {
	repositories = make([]Repository, 0)
	err = g.callGithubApi("GET", "/user/repos", &repositories)
	return
}

// Get the user's repositories.
func (u User) Repositories() (repositories []Repository, err error) {
	repositories = make([]Repository, 0)
	uri := fmt.Sprintf("/users/%s/repos", u.Login)
	err = u.g.callGithubApi("GET", uri, &repositories)
	return
}
<commit_msg>Finished mapping out the fields from the JSON response for a repository, to the Repository struct.<commit_after>package gothub

import (
	"fmt"
	"time"
)

type RepositoryOwner struct {
	AvatarUrl        string `json:"avatar_url"`
	EventsUrl        string `json:"events_url"`
	FollowersUrl     string `json:"followers_url"`
	FollowingUrl     string `json:"following_url"`
	GistsUrl         string `json:"gists_url"`
	GravatarId       string `json:"gravatar_id"`
	HtmlUrl          string `json:"html_url"`
	Id               int    `json:"id"`
	Login            string `json:"login"`
	OrganizationsUrl string `json:"organizations_url"`
	ReposUrl         string `json:"repos_url"`
	StarredUrl       string `json:"starred_url"`
	SubscriptionsUrl string `json:"subscriptions_url"`
	Type             string `json:"type"`
	Url              string `json:"url"`
}

type RepositoryPermissions struct {
	Admin bool `json:"admin"`
	Pull  bool `json:"pull"`
	Push  bool `json:"push"`
}

type Repository struct {
	ArchiveUrl       string                `json:"archive_url"`
	AssigneesUrl     string                `json:"assignees_url"`
	BlobsUrl         string                `json:"blobs_url"`
	CloneUrl         string                `json:"clone_url"`
	CollaboratorsUrl string                `json:"collaborators_url"`
	CommentsUrl      string                `json:"comments_url"`
	CommitsUrl       string                `json:"comments_url"`
	CompareUrl       string                `json:"compare_url"`
	ContentsUrl      string                `json:"contentsUrl"`
	ContributorsUrl  string                `json:"contributors_url"`
	CreatedAt        time.Time             `json:"created_at"`
	DefaultBranch    string                `json:"default_branch"`
	Description      string                `json:"description"`
	DownloadsUrl     string                `json:"downloads_url"`
	EventsUrl        string                `json:"events_url"`
	Fork             bool                  `json:"fork"`
	Forks            int                   `json:"forks"`
	ForksCount       int                   `json:"forks_count"`
	ForksUrl         string                `json:"forks_url"`
	FullName         string                `json:"full_name"`
	GitCommitsUrl    string                `json:"git_commits_url"`
	GitRefsUrl       string                `json:"git_refs_url"`
	GitTagsUrl       string                `json:"git_tags_url"`
	GitUrl           string                `json:"git_url"`
	HasDownloads     bool                  `json:"has_downloads"`
	HasIssues        bool                  `json:"has_issues"`
	HasWiki          bool                  `json:"has_wiki"`
	Homepage         string                `json:"homepage"`
	HooksUrl         string                `json:"hooks_url"`
	HtmlUrl          string                `json:"html_url"`
	Id               int                   `json:"id"`
	IssueCommentUrl  string                `json:"issue_comment_url"`
	IssueEventsUrl   string                `json:"issue_events_url"`
	IssuesUrl        string                `json:"issues_url"`
	KeysUrl          string                `json:"keys_url"`
	LabelsUrl        string                `json:"labels_url"`
	Language         string                `json:"language"`
	LanguagesUrl     string                `json:"languages_url"`
	MasterBranch     string                `json:"master_branch"`
	MergesUrl        string                `json:"merges_url"`
	MilestonesUrl    string                `json:"milestones_url"`
	MirrorUrl        string                `json:"mirror_url"`
	Name             string                `json:"name"`
	NotificationsUrl string                `json:"notifications_url"`
	OpenIssues       int                   `json:"open_issues"`
	OpenIssuesCount  int                   `json:"open_issues_count"`
	Owner            RepositoryOwner       `json:"owner"`
	Permissions      RepositoryPermissions `json:"permissions"`
	Private          bool                  `json:"private"`
	PullsUrl         string                `json:"pulls_url"`
	PushedAt         time.Time             `json:"pushed_at"`
	Size             int                   `json:"size"`
	SshUrl           string                `json:"ssh_url"`
	StargazersUrl    string                `json:"stargazers_url"`
	StatusesUrl      string                `json:"statuses_url"`
	SubscribersUrl   string                `json:"subscribers_url"`
	SubscriptionUrl  string                `json:"subscription_url"`
	SvnUrl           string                `json:"svn_url"`
	TagsUrl          string                `json:"tags_url"`
	TeamsUrl         string                `json:"teams_url"`
	TreesUrl         string                `json:"trees_url"`
	UpdatedAt        time.Time             `json:"updated_at"`
	Url              string                `json:"url"`
	Watchers         int                   `json:"watchers"`
	WatchersCount    int                   `json:"watchers_count"`
}

// Get the currently-authenticated user's repositories.
func (g GitHub) Repositories() (repositories []Repository, err error) {
	repositories = make([]Repository, 0)
	err = g.callGithubApi("GET", "/user/repos", &repositories)
	return
}

// Get the user's repositories.
func (u User) Repositories() (repositories []Repository, err error) {
	repositories = make([]Repository, 0)
	uri := fmt.Sprintf("/users/%s/repos", u.Login)
	err = u.g.callGithubApi("GET", uri, &repositories)
	return
}
<|endoftext|>


<commit_before>package xmlrpc

import (
    "testing"
)

func Test_buildValueElement_EscapesString(t *testing.T) {
    escaped := buildValueElement("Johnson & Johnson > 1")
    assert_equal(t, "<value><string>Johnson & Johnson > 1</string></value>", escaped)
}
<commit_msg>Add unit test for multiple parameter requests<commit_after>package xmlrpc

import (
	"encoding/xml"
	"testing"
)

func Test_buildValueElement_EscapesString(t *testing.T) {
	escaped := buildValueElement("Johnson & Johnson > 1")
	assert_equal(t, "<value><string>Johnson & Johnson > 1</string></value>", escaped)
}

const PARAMS_REQUEST = `
<?xml version="1.0" encoding="UTF-8"?>
<methodCall>
  <methodName>method</methodName>
  <params>
    <param>
      <value>
        <string>user</string>
      </value>
    </param>
    <param>
      <value>
        <string>pass</string>
      </value>
    </param>
  </params>
</methodCall>
`

type MethodCall struct {
	XMLName    xml.Name   `xml:"methodCall"`
	MethodName string     `xml:"methodName"`
	Params     []ParamsTy `xml:"params>param"`
}

type ParamsTy struct {
	Value string `xml:"value>string"`
}

func Test_buildRequestBody(t *testing.T) {
	var xml_expected MethodCall
	xml.Unmarshal([]byte(PARAMS_REQUEST), &xml_expected)

	params := Params{Params: []interface{}{"user", "pass"}}
	request := buildRequestBody("method", []interface{}{params})

	var xml_request MethodCall
	xml.Unmarshal([]byte(request), &xml_request)

	assert_equal(t, xml_request.MethodName, xml_expected.MethodName)
	assert_equal(t, xml_request.Params[0], xml_expected.Params[0])
	assert_equal(t, xml_request.Params[1], xml_expected.Params[1])
}
<|endoftext|>


<commit_before>package main

import (
	"testing"
)

var reverseDefaultTests = []struct {
	src string
	dst string
}{
	{"abcd", "dcba"},
	{" \t\n", "\n\t "},
	{"日本語", "語本日"},
	{"10,200,3000", "0003,002,01"},
}

func TestReverseDefault(t *testing.T) {
	rev := NewReverser()
	for _, test := range reverseDefaultTests {
		expect := test.dst
		actual := rev.Reverse(test.src)
		if actual != expect {
			t.Errorf("Reverse(%q) = %q, want %q",
				test.src, actual, expect)
		}
	}
}
<commit_msg>Add test for Reverse.Reverse with delimiter<commit_after>package main

import (
	"testing"
)

var reverseDefaultTests = []struct {
	src string
	dst string
}{
	{"abcd", "dcba"},
	{" \t\n", "\n\t "},
	{"日本語", "語本日"},
	{"10,200,3000", "0003,002,01"},
}

func TestReverseDefault(t *testing.T) {
	rev := NewReverser()
	for _, test := range reverseDefaultTests {
		expect := test.dst
		actual := rev.Reverse(test.src)
		if actual != expect {
			t.Errorf("Reverse(%q) = %q, want %q",
				test.src, actual, expect)
		}
	}
}

var reverseWithDelimiterTests = []struct {
	delimiter string
	src       string
	dst       string
}{
	{",", "abcd", "abcd"},
	{",", "10,200,3000", "3000,200,10"},
	{"、", "十、二百、三千", "三千、二百、十"},
}

func TestReverseWithDelimiter(t *testing.T) {
	rev := NewReverser()
	for _, test := range reverseWithDelimiterTests {
		rev.SetDelimiter(test.delimiter)
		expect := test.dst
		actual := rev.Reverse(test.src)
		if actual != expect {
			t.Errorf("Reverse(%q) = %q, want %q",
				test.src, actual, expect)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 The Go 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 net

import "sync/atomic"

// fdMutex is a specialized synchronization primitive
// that manages lifetime of an fd and serializes access
// to Read and Write methods on netFD.
type fdMutex struct {
	state uint64
	rsema uint32
	wsema uint32
}

// fdMutex.state is organized as follows:
// 1 bit - whether netFD is closed, if set all subsequent lock operations will fail.
// 1 bit - lock for read operations.
// 1 bit - lock for write operations.
// 20 bits - total number of references (read+write+misc).
// 20 bits - number of outstanding read waiters.
// 20 bits - number of outstanding write waiters.
const (
	mutexClosed  = 1 << 0
	mutexRLock   = 1 << 1
	mutexWLock   = 1 << 2
	mutexRef     = 1 << 3
	mutexRefMask = (1<<20 - 1) << 3
	mutexRWait   = 1 << 23
	mutexRMask   = (1<<20 - 1) << 23
	mutexWWait   = 1 << 43
	mutexWMask   = (1<<20 - 1) << 43
)

// Read operations must do RWLock(true)/RWUnlock(true).
// Write operations must do RWLock(false)/RWUnlock(false).
// Misc operations must do Incref/Decref. Misc operations include functions like
// setsockopt and setDeadline. They need to use Incref/Decref to ensure that
// they operate on the correct fd in presence of a concurrent Close call
// (otherwise fd can be closed under their feet).
// Close operation must do IncrefAndClose/Decref.

// RWLock/Incref return whether fd is open.
// RWUnlock/Decref return whether fd is closed and there are no remaining references.

func (mu *fdMutex) Incref() bool {
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexClosed != 0 {
			return false
		}
		new := old + mutexRef
		if new&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			return true
		}
	}
}

func (mu *fdMutex) IncrefAndClose() bool {
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexClosed != 0 {
			return false
		}
		// Mark as closed and acquire a reference.
		new := (old | mutexClosed) + mutexRef
		if new&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		// Remove all read and write waiters.
		new &^= mutexRMask | mutexWMask
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			// Wake all read and write waiters,
			// they will observe closed flag after wakeup.
			for old&mutexRMask != 0 {
				old -= mutexRWait
				runtime_Semrelease(&mu.rsema)
			}
			for old&mutexWMask != 0 {
				old -= mutexWWait
				runtime_Semrelease(&mu.wsema)
			}
			return true
		}
	}
}

func (mu *fdMutex) Decref() bool {
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		new := old - mutexRef
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			return new&(mutexClosed|mutexRef) == mutexClosed
		}
	}
}

func (mu *fdMutex) RWLock(read bool) bool {
	var mutexBit, mutexWait, mutexMask uint64
	var mutexSema *uint32
	if read {
		mutexBit = mutexRLock
		mutexWait = mutexRWait
		mutexMask = mutexRMask
		mutexSema = &mu.rsema
	} else {
		mutexBit = mutexWLock
		mutexWait = mutexWWait
		mutexMask = mutexWMask
		mutexSema = &mu.wsema
	}
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexClosed != 0 {
			return false
		}
		var new uint64
		if old&mutexBit == 0 {
			// Lock is free, acquire it.
			new = (old | mutexBit) + mutexRef
			if new&mutexRefMask == 0 {
				panic("net: inconsistent fdMutex")
			}
		} else {
			// Wait for lock.
			new = old + mutexWait
			if new&mutexMask == 0 {
				panic("net: inconsistent fdMutex")
			}
		}
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			if old&mutexBit == 0 {
				return true
			}
			runtime_Semacquire(mutexSema)
			// The signaller has subtracted mutexWait.
		}
	}
}

func (mu *fdMutex) RWUnlock(read bool) bool {
	var mutexBit, mutexWait, mutexMask uint64
	var mutexSema *uint32
	if read {
		mutexBit = mutexRLock
		mutexWait = mutexRWait
		mutexMask = mutexRMask
		mutexSema = &mu.rsema
	} else {
		mutexBit = mutexWLock
		mutexWait = mutexWWait
		mutexMask = mutexWMask
		mutexSema = &mu.wsema
	}
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexBit == 0 || old&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		// Drop lock, drop reference and wake read waiter if present.
		new := (old &^ mutexBit) - mutexRef
		if old&mutexMask != 0 {
			new -= mutexWait
		}
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			if old&mutexMask != 0 {
				runtime_Semrelease(mutexSema)
			}
			return new&(mutexClosed|mutexRef) == mutexClosed
		}
	}
}

// Implemented in runtime package.
func runtime_Semacquire(sema *uint32)
func runtime_Semrelease(sema *uint32)
<commit_msg>net: fix bug in fdMutex Fixes issue 6165.<commit_after>// Copyright 2013 The Go 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 net

import "sync/atomic"

// fdMutex is a specialized synchronization primitive
// that manages lifetime of an fd and serializes access
// to Read and Write methods on netFD.
type fdMutex struct {
	state uint64
	rsema uint32
	wsema uint32
}

// fdMutex.state is organized as follows:
// 1 bit - whether netFD is closed, if set all subsequent lock operations will fail.
// 1 bit - lock for read operations.
// 1 bit - lock for write operations.
// 20 bits - total number of references (read+write+misc).
// 20 bits - number of outstanding read waiters.
// 20 bits - number of outstanding write waiters.
const (
	mutexClosed  = 1 << 0
	mutexRLock   = 1 << 1
	mutexWLock   = 1 << 2
	mutexRef     = 1 << 3
	mutexRefMask = (1<<20 - 1) << 3
	mutexRWait   = 1 << 23
	mutexRMask   = (1<<20 - 1) << 23
	mutexWWait   = 1 << 43
	mutexWMask   = (1<<20 - 1) << 43
)

// Read operations must do RWLock(true)/RWUnlock(true).
// Write operations must do RWLock(false)/RWUnlock(false).
// Misc operations must do Incref/Decref. Misc operations include functions like
// setsockopt and setDeadline. They need to use Incref/Decref to ensure that
// they operate on the correct fd in presence of a concurrent Close call
// (otherwise fd can be closed under their feet).
// Close operation must do IncrefAndClose/Decref.

// RWLock/Incref return whether fd is open.
// RWUnlock/Decref return whether fd is closed and there are no remaining references.

func (mu *fdMutex) Incref() bool {
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexClosed != 0 {
			return false
		}
		new := old + mutexRef
		if new&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			return true
		}
	}
}

func (mu *fdMutex) IncrefAndClose() bool {
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexClosed != 0 {
			return false
		}
		// Mark as closed and acquire a reference.
		new := (old | mutexClosed) + mutexRef
		if new&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		// Remove all read and write waiters.
		new &^= mutexRMask | mutexWMask
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			// Wake all read and write waiters,
			// they will observe closed flag after wakeup.
			for old&mutexRMask != 0 {
				old -= mutexRWait
				runtime_Semrelease(&mu.rsema)
			}
			for old&mutexWMask != 0 {
				old -= mutexWWait
				runtime_Semrelease(&mu.wsema)
			}
			return true
		}
	}
}

func (mu *fdMutex) Decref() bool {
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		new := old - mutexRef
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			return new&(mutexClosed|mutexRefMask) == mutexClosed
		}
	}
}

func (mu *fdMutex) RWLock(read bool) bool {
	var mutexBit, mutexWait, mutexMask uint64
	var mutexSema *uint32
	if read {
		mutexBit = mutexRLock
		mutexWait = mutexRWait
		mutexMask = mutexRMask
		mutexSema = &mu.rsema
	} else {
		mutexBit = mutexWLock
		mutexWait = mutexWWait
		mutexMask = mutexWMask
		mutexSema = &mu.wsema
	}
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexClosed != 0 {
			return false
		}
		var new uint64
		if old&mutexBit == 0 {
			// Lock is free, acquire it.
			new = (old | mutexBit) + mutexRef
			if new&mutexRefMask == 0 {
				panic("net: inconsistent fdMutex")
			}
		} else {
			// Wait for lock.
			new = old + mutexWait
			if new&mutexMask == 0 {
				panic("net: inconsistent fdMutex")
			}
		}
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			if old&mutexBit == 0 {
				return true
			}
			runtime_Semacquire(mutexSema)
			// The signaller has subtracted mutexWait.
		}
	}
}

func (mu *fdMutex) RWUnlock(read bool) bool {
	var mutexBit, mutexWait, mutexMask uint64
	var mutexSema *uint32
	if read {
		mutexBit = mutexRLock
		mutexWait = mutexRWait
		mutexMask = mutexRMask
		mutexSema = &mu.rsema
	} else {
		mutexBit = mutexWLock
		mutexWait = mutexWWait
		mutexMask = mutexWMask
		mutexSema = &mu.wsema
	}
	for {
		old := atomic.LoadUint64(&mu.state)
		if old&mutexBit == 0 || old&mutexRefMask == 0 {
			panic("net: inconsistent fdMutex")
		}
		// Drop lock, drop reference and wake read waiter if present.
		new := (old &^ mutexBit) - mutexRef
		if old&mutexMask != 0 {
			new -= mutexWait
		}
		if atomic.CompareAndSwapUint64(&mu.state, old, new) {
			if old&mutexMask != 0 {
				runtime_Semrelease(mutexSema)
			}
			return new&(mutexClosed|mutexRefMask) == mutexClosed
		}
	}
}

// Implemented in runtime package.
func runtime_Semacquire(sema *uint32)
func runtime_Semrelease(sema *uint32)
<|endoftext|>


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

// Binary to decimal floating point conversion.
// Algorithm:
//   1) store mantissa in multiprecision decimal
//   2) shift decimal by exponent
//   3) read digits out & format

package strconv

import "math"

// TODO: move elsewhere?
type floatInfo struct {
	mantbits uint
	expbits  uint
	bias     int
}

var float32info = floatInfo{23, 8, -127}
var float64info = floatInfo{52, 11, -1023}

// Ftoa32 converts the 32-bit floating-point number f to a string,
// according to the format fmt and precision prec.
//
// The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent),
// 'e' (-d.dddde±dd, a decimal exponent),
// 'E' (-d.ddddE±dd, a decimal exponent),
// 'f' (-ddd.dddd, no exponent),
// 'g' ('e' for large exponents, 'f' otherwise), or
// 'G' ('E' for large exponents, 'f' otherwise).
//
// The precision prec controls the number of digits
// (excluding the exponent) printed by the 'e', 'E', 'f', 'g', and 'G' formats.
// For 'e', 'E', and 'f' it is the number of digits after the decimal point.
// For 'g' and 'G' it is the total number of digits.
// The special precision -1 uses the smallest number of digits
// necessary such that Atof32 will return f exactly.
//
// Ftoa32(f) is not the same as Ftoa64(float32(f)),
// because correct rounding and the number of digits
// needed to identify f depend on the precision of the representation.
func Ftoa32(f float32, fmt byte, prec int) string {
	return genericFtoa(uint64(math.Float32bits(f)), fmt, prec, &float32info)
}

// Ftoa64 is like Ftoa32 but converts a 64-bit floating-point number.
func Ftoa64(f float64, fmt byte, prec int) string {
	return genericFtoa(math.Float64bits(f), fmt, prec, &float64info)
}

// FtoaN converts the 64-bit floating-point number f to a string,
// according to the format fmt and precision prec, but it rounds the
// result assuming that it was obtained from a floating-point value
// of n bits (32 or 64).
func FtoaN(f float64, fmt byte, prec int, n int) string {
	if n == 32 {
		return Ftoa32(float32(f), fmt, prec)
	}
	return Ftoa64(f, fmt, prec)
}

func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
	neg := bits>>flt.expbits>>flt.mantbits != 0
	exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
	mant := bits & (uint64(1)<<flt.mantbits - 1)

	switch exp {
	case 1<<flt.expbits - 1:
		// Inf, NaN
		if mant != 0 {
			return "NaN"
		}
		if neg {
			return "-Inf"
		}
		return "+Inf"

	case 0:
		// denormalized
		exp++

	default:
		// add implicit top bit
		mant |= uint64(1) << flt.mantbits
	}
	exp += flt.bias

	// Pick off easy binary format.
	if fmt == 'b' {
		return fmtB(neg, mant, exp, flt)
	}

	// Create exact decimal representation.
	// The shift is exp - flt.mantbits because mant is a 1-bit integer
	// followed by a flt.mantbits fraction, and we are treating it as
	// a 1+flt.mantbits-bit integer.
	d := newDecimal(mant).Shift(exp - int(flt.mantbits))

	// Round appropriately.
	// Negative precision means "only as much as needed to be exact."
	shortest := false
	if prec < 0 {
		shortest = true
		roundShortest(d, mant, exp, flt)
		switch fmt {
		case 'e', 'E':
			prec = d.nd - 1
		case 'f':
			prec = max(d.nd-d.dp, 0)
		case 'g', 'G':
			prec = d.nd
		}
	} else {
		switch fmt {
		case 'e', 'E':
			d.Round(prec + 1)
		case 'f':
			d.Round(d.dp + prec)
		case 'g', 'G':
			if prec == 0 {
				prec = 1
			}
			d.Round(prec)
		}
	}

	switch fmt {
	case 'e', 'E':
		return fmtE(neg, d, prec, fmt)
	case 'f':
		return fmtF(neg, d, prec)
	case 'g', 'G':
		// trailing fractional zeros in 'e' form will be trimmed.
		eprec := prec
		if eprec > d.nd && d.nd >= d.dp {
			eprec = d.nd
		}
		// %e is used if the exponent from the conversion
		// is less than -4 or greater than or equal to the precision.
		// if precision was the shortest possible, use precision 6 for this decision.
		if shortest {
			eprec = 6
		}
		exp := d.dp - 1
		if exp < -4 || exp >= eprec {
			if prec > d.nd {
				prec = d.nd
			}
			return fmtE(neg, d, prec-1, fmt+'e'-'g')
		}
		if prec > d.dp {
			prec = d.nd
		}
		return fmtF(neg, d, max(prec-d.dp, 0))
	}

	return "%" + string(fmt)
}

// Round d (= mant * 2^exp) to the shortest number of digits
// that will let the original floating point value be precisely
// reconstructed.  Size is original floating point size (64 or 32).
func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
	// If mantissa is zero, the number is zero; stop now.
	if mant == 0 {
		d.nd = 0
		return
	}

	// TODO(rsc): Unless exp == minexp, if the number of digits in d
	// is less than 17, it seems likely that it would be
	// the shortest possible number already.  So maybe we can
	// bail out without doing the extra multiprecision math here.

	// Compute upper and lower such that any decimal number
	// between upper and lower (possibly inclusive)
	// will round to the original floating point number.

	// d = mant << (exp - mantbits)
	// Next highest floating point number is mant+1 << exp-mantbits.
	// Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
	upper := newDecimal(mant*2 + 1).Shift(exp - int(flt.mantbits) - 1)

	// d = mant << (exp - mantbits)
	// Next lowest floating point number is mant-1 << exp-mantbits,
	// unless mant-1 drops the significant bit and exp is not the minimum exp,
	// in which case the next lowest is mant*2-1 << exp-mantbits-1.
	// Either way, call it mantlo << explo-mantbits.
	// Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
	minexp := flt.bias + 1 // minimum possible exponent
	var mantlo uint64
	var explo int
	if mant > 1<<flt.mantbits || exp == minexp {
		mantlo = mant - 1
		explo = exp
	} else {
		mantlo = mant*2 - 1
		explo = exp - 1
	}
	lower := newDecimal(mantlo*2 + 1).Shift(explo - int(flt.mantbits) - 1)

	// The upper and lower bounds are possible outputs only if
	// the original mantissa is even, so that IEEE round-to-even
	// would round to the original mantissa and not the neighbors.
	inclusive := mant%2 == 0

	// Now we can figure out the minimum number of digits required.
	// Walk along until d has distinguished itself from upper and lower.
	for i := 0; i < d.nd; i++ {
		var l, m, u byte // lower, middle, upper digits
		if i < lower.nd {
			l = lower.d[i]
		} else {
			l = '0'
		}
		m = d.d[i]
		if i < upper.nd {
			u = upper.d[i]
		} else {
			u = '0'
		}

		// Okay to round down (truncate) if lower has a different digit
		// or if lower is inclusive and is exactly the result of rounding down.
		okdown := l != m || (inclusive && l == m && i+1 == lower.nd)

		// Okay to round up if upper has a different digit and
		// either upper is inclusive or upper is bigger than the result of rounding up.
		okup := m != u && (inclusive || i+1 < upper.nd)

		// If it's okay to do either, then round to the nearest one.
		// If it's okay to do only one, do it.
		switch {
		case okdown && okup:
			d.Round(i + 1)
			return
		case okdown:
			d.RoundDown(i + 1)
			return
		case okup:
			d.RoundUp(i + 1)
			return
		}
	}
}

// %e: -d.ddddde±dd
func fmtE(neg bool, d *decimal, prec int, fmt byte) string {
	buf := make([]byte, 3+max(prec, 0)+30) // "-0." + prec digits + exp
	w := 0                                 // write index

	// sign
	if neg {
		buf[w] = '-'
		w++
	}

	// first digit
	if d.nd == 0 {
		buf[w] = '0'
	} else {
		buf[w] = d.d[0]
	}
	w++

	// .moredigits
	if prec > 0 {
		buf[w] = '.'
		w++
		for i := 0; i < prec; i++ {
			if 1+i < d.nd {
				buf[w] = d.d[1+i]
			} else {
				buf[w] = '0'
			}
			w++
		}
	}

	// e±
	buf[w] = fmt
	w++
	exp := d.dp - 1
	if d.nd == 0 { // special case: 0 has exponent 0
		exp = 0
	}
	if exp < 0 {
		buf[w] = '-'
		exp = -exp
	} else {
		buf[w] = '+'
	}
	w++

	// dddd
	// count digits
	n := 0
	for e := exp; e > 0; e /= 10 {
		n++
	}
	// leading zeros
	for i := n; i < 2; i++ {
		buf[w] = '0'
		w++
	}
	// digits
	w += n
	n = 0
	for e := exp; e > 0; e /= 10 {
		n++
		buf[w-n] = byte(e%10 + '0')
	}

	return string(buf[0:w])
}

// %f: -ddddddd.ddddd
func fmtF(neg bool, d *decimal, prec int) string {
	buf := make([]byte, 1+max(d.dp, 1)+1+max(prec, 0))
	w := 0

	// sign
	if neg {
		buf[w] = '-'
		w++
	}

	// integer, padded with zeros as needed.
	if d.dp > 0 {
		var i int
		for i = 0; i < d.dp && i < d.nd; i++ {
			buf[w] = d.d[i]
			w++
		}
		for ; i < d.dp; i++ {
			buf[w] = '0'
			w++
		}
	} else {
		buf[w] = '0'
		w++
	}

	// fraction
	if prec > 0 {
		buf[w] = '.'
		w++
		for i := 0; i < prec; i++ {
			if d.dp+i < 0 || d.dp+i >= d.nd {
				buf[w] = '0'
			} else {
				buf[w] = d.d[d.dp+i]
			}
			w++
		}
	}

	return string(buf[0:w])
}

// %b: -ddddddddp+ddd
func fmtB(neg bool, mant uint64, exp int, flt *floatInfo) string {
	var buf [50]byte
	w := len(buf)
	exp -= int(flt.mantbits)
	esign := byte('+')
	if exp < 0 {
		esign = '-'
		exp = -exp
	}
	n := 0
	for exp > 0 || n < 1 {
		n++
		w--
		buf[w] = byte(exp%10 + '0')
		exp /= 10
	}
	w--
	buf[w] = esign
	w--
	buf[w] = 'p'
	n = 0
	for mant > 0 || n < 1 {
		n++
		w--
		buf[w] = byte(mant%10 + '0')
		mant /= 10
	}
	if neg {
		w--
		buf[w] = '-'
	}
	return string(buf[w:])
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
<commit_msg>strconv/ftoa: avoid a double shift. (shifts by variables are expensive.)<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Binary to decimal floating point conversion.
// Algorithm:
//   1) store mantissa in multiprecision decimal
//   2) shift decimal by exponent
//   3) read digits out & format

package strconv

import "math"

// TODO: move elsewhere?
type floatInfo struct {
	mantbits uint
	expbits  uint
	bias     int
}

var float32info = floatInfo{23, 8, -127}
var float64info = floatInfo{52, 11, -1023}

// Ftoa32 converts the 32-bit floating-point number f to a string,
// according to the format fmt and precision prec.
//
// The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent),
// 'e' (-d.dddde±dd, a decimal exponent),
// 'E' (-d.ddddE±dd, a decimal exponent),
// 'f' (-ddd.dddd, no exponent),
// 'g' ('e' for large exponents, 'f' otherwise), or
// 'G' ('E' for large exponents, 'f' otherwise).
//
// The precision prec controls the number of digits
// (excluding the exponent) printed by the 'e', 'E', 'f', 'g', and 'G' formats.
// For 'e', 'E', and 'f' it is the number of digits after the decimal point.
// For 'g' and 'G' it is the total number of digits.
// The special precision -1 uses the smallest number of digits
// necessary such that Atof32 will return f exactly.
//
// Ftoa32(f) is not the same as Ftoa64(float32(f)),
// because correct rounding and the number of digits
// needed to identify f depend on the precision of the representation.
func Ftoa32(f float32, fmt byte, prec int) string {
	return genericFtoa(uint64(math.Float32bits(f)), fmt, prec, &float32info)
}

// Ftoa64 is like Ftoa32 but converts a 64-bit floating-point number.
func Ftoa64(f float64, fmt byte, prec int) string {
	return genericFtoa(math.Float64bits(f), fmt, prec, &float64info)
}

// FtoaN converts the 64-bit floating-point number f to a string,
// according to the format fmt and precision prec, but it rounds the
// result assuming that it was obtained from a floating-point value
// of n bits (32 or 64).
func FtoaN(f float64, fmt byte, prec int, n int) string {
	if n == 32 {
		return Ftoa32(float32(f), fmt, prec)
	}
	return Ftoa64(f, fmt, prec)
}

func genericFtoa(bits uint64, fmt byte, prec int, flt *floatInfo) string {
	neg := bits>>(flt.expbits+flt.mantbits) != 0
	exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
	mant := bits & (uint64(1)<<flt.mantbits - 1)

	switch exp {
	case 1<<flt.expbits - 1:
		// Inf, NaN
		if mant != 0 {
			return "NaN"
		}
		if neg {
			return "-Inf"
		}
		return "+Inf"

	case 0:
		// denormalized
		exp++

	default:
		// add implicit top bit
		mant |= uint64(1) << flt.mantbits
	}
	exp += flt.bias

	// Pick off easy binary format.
	if fmt == 'b' {
		return fmtB(neg, mant, exp, flt)
	}

	// Create exact decimal representation.
	// The shift is exp - flt.mantbits because mant is a 1-bit integer
	// followed by a flt.mantbits fraction, and we are treating it as
	// a 1+flt.mantbits-bit integer.
	d := newDecimal(mant).Shift(exp - int(flt.mantbits))

	// Round appropriately.
	// Negative precision means "only as much as needed to be exact."
	shortest := false
	if prec < 0 {
		shortest = true
		roundShortest(d, mant, exp, flt)
		switch fmt {
		case 'e', 'E':
			prec = d.nd - 1
		case 'f':
			prec = max(d.nd-d.dp, 0)
		case 'g', 'G':
			prec = d.nd
		}
	} else {
		switch fmt {
		case 'e', 'E':
			d.Round(prec + 1)
		case 'f':
			d.Round(d.dp + prec)
		case 'g', 'G':
			if prec == 0 {
				prec = 1
			}
			d.Round(prec)
		}
	}

	switch fmt {
	case 'e', 'E':
		return fmtE(neg, d, prec, fmt)
	case 'f':
		return fmtF(neg, d, prec)
	case 'g', 'G':
		// trailing fractional zeros in 'e' form will be trimmed.
		eprec := prec
		if eprec > d.nd && d.nd >= d.dp {
			eprec = d.nd
		}
		// %e is used if the exponent from the conversion
		// is less than -4 or greater than or equal to the precision.
		// if precision was the shortest possible, use precision 6 for this decision.
		if shortest {
			eprec = 6
		}
		exp := d.dp - 1
		if exp < -4 || exp >= eprec {
			if prec > d.nd {
				prec = d.nd
			}
			return fmtE(neg, d, prec-1, fmt+'e'-'g')
		}
		if prec > d.dp {
			prec = d.nd
		}
		return fmtF(neg, d, max(prec-d.dp, 0))
	}

	return "%" + string(fmt)
}

// Round d (= mant * 2^exp) to the shortest number of digits
// that will let the original floating point value be precisely
// reconstructed.  Size is original floating point size (64 or 32).
func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
	// If mantissa is zero, the number is zero; stop now.
	if mant == 0 {
		d.nd = 0
		return
	}

	// TODO(rsc): Unless exp == minexp, if the number of digits in d
	// is less than 17, it seems likely that it would be
	// the shortest possible number already.  So maybe we can
	// bail out without doing the extra multiprecision math here.

	// Compute upper and lower such that any decimal number
	// between upper and lower (possibly inclusive)
	// will round to the original floating point number.

	// d = mant << (exp - mantbits)
	// Next highest floating point number is mant+1 << exp-mantbits.
	// Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
	upper := newDecimal(mant*2 + 1).Shift(exp - int(flt.mantbits) - 1)

	// d = mant << (exp - mantbits)
	// Next lowest floating point number is mant-1 << exp-mantbits,
	// unless mant-1 drops the significant bit and exp is not the minimum exp,
	// in which case the next lowest is mant*2-1 << exp-mantbits-1.
	// Either way, call it mantlo << explo-mantbits.
	// Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
	minexp := flt.bias + 1 // minimum possible exponent
	var mantlo uint64
	var explo int
	if mant > 1<<flt.mantbits || exp == minexp {
		mantlo = mant - 1
		explo = exp
	} else {
		mantlo = mant*2 - 1
		explo = exp - 1
	}
	lower := newDecimal(mantlo*2 + 1).Shift(explo - int(flt.mantbits) - 1)

	// The upper and lower bounds are possible outputs only if
	// the original mantissa is even, so that IEEE round-to-even
	// would round to the original mantissa and not the neighbors.
	inclusive := mant%2 == 0

	// Now we can figure out the minimum number of digits required.
	// Walk along until d has distinguished itself from upper and lower.
	for i := 0; i < d.nd; i++ {
		var l, m, u byte // lower, middle, upper digits
		if i < lower.nd {
			l = lower.d[i]
		} else {
			l = '0'
		}
		m = d.d[i]
		if i < upper.nd {
			u = upper.d[i]
		} else {
			u = '0'
		}

		// Okay to round down (truncate) if lower has a different digit
		// or if lower is inclusive and is exactly the result of rounding down.
		okdown := l != m || (inclusive && l == m && i+1 == lower.nd)

		// Okay to round up if upper has a different digit and
		// either upper is inclusive or upper is bigger than the result of rounding up.
		okup := m != u && (inclusive || i+1 < upper.nd)

		// If it's okay to do either, then round to the nearest one.
		// If it's okay to do only one, do it.
		switch {
		case okdown && okup:
			d.Round(i + 1)
			return
		case okdown:
			d.RoundDown(i + 1)
			return
		case okup:
			d.RoundUp(i + 1)
			return
		}
	}
}

// %e: -d.ddddde±dd
func fmtE(neg bool, d *decimal, prec int, fmt byte) string {
	buf := make([]byte, 3+max(prec, 0)+30) // "-0." + prec digits + exp
	w := 0                                 // write index

	// sign
	if neg {
		buf[w] = '-'
		w++
	}

	// first digit
	if d.nd == 0 {
		buf[w] = '0'
	} else {
		buf[w] = d.d[0]
	}
	w++

	// .moredigits
	if prec > 0 {
		buf[w] = '.'
		w++
		for i := 0; i < prec; i++ {
			if 1+i < d.nd {
				buf[w] = d.d[1+i]
			} else {
				buf[w] = '0'
			}
			w++
		}
	}

	// e±
	buf[w] = fmt
	w++
	exp := d.dp - 1
	if d.nd == 0 { // special case: 0 has exponent 0
		exp = 0
	}
	if exp < 0 {
		buf[w] = '-'
		exp = -exp
	} else {
		buf[w] = '+'
	}
	w++

	// dddd
	// count digits
	n := 0
	for e := exp; e > 0; e /= 10 {
		n++
	}
	// leading zeros
	for i := n; i < 2; i++ {
		buf[w] = '0'
		w++
	}
	// digits
	w += n
	n = 0
	for e := exp; e > 0; e /= 10 {
		n++
		buf[w-n] = byte(e%10 + '0')
	}

	return string(buf[0:w])
}

// %f: -ddddddd.ddddd
func fmtF(neg bool, d *decimal, prec int) string {
	buf := make([]byte, 1+max(d.dp, 1)+1+max(prec, 0))
	w := 0

	// sign
	if neg {
		buf[w] = '-'
		w++
	}

	// integer, padded with zeros as needed.
	if d.dp > 0 {
		var i int
		for i = 0; i < d.dp && i < d.nd; i++ {
			buf[w] = d.d[i]
			w++
		}
		for ; i < d.dp; i++ {
			buf[w] = '0'
			w++
		}
	} else {
		buf[w] = '0'
		w++
	}

	// fraction
	if prec > 0 {
		buf[w] = '.'
		w++
		for i := 0; i < prec; i++ {
			if d.dp+i < 0 || d.dp+i >= d.nd {
				buf[w] = '0'
			} else {
				buf[w] = d.d[d.dp+i]
			}
			w++
		}
	}

	return string(buf[0:w])
}

// %b: -ddddddddp+ddd
func fmtB(neg bool, mant uint64, exp int, flt *floatInfo) string {
	var buf [50]byte
	w := len(buf)
	exp -= int(flt.mantbits)
	esign := byte('+')
	if exp < 0 {
		esign = '-'
		exp = -exp
	}
	n := 0
	for exp > 0 || n < 1 {
		n++
		w--
		buf[w] = byte(exp%10 + '0')
		exp /= 10
	}
	w--
	buf[w] = esign
	w--
	buf[w] = 'p'
	n = 0
	for mant > 0 || n < 1 {
		n++
		w--
		buf[w] = byte(mant%10 + '0')
		mant /= 10
	}
	if neg {
		w--
		buf[w] = '-'
	}
	return string(buf[w:])
}

func max(a, b int) int {
	if a > b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package gpio

import (
	"errors"
	"strings"
	"testing"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/gobottest"
)

var _ gobot.Driver = (*DirectPinDriver)(nil)

func initTestDirectPinDriver() *DirectPinDriver {
	a := newGpioTestAdaptor()
	a.testAdaptorDigitalRead = func() (val int, err error) {
		val = 1
		return
	}
	a.testAdaptorDigitalWrite = func() (err error) {
		return errors.New("write error")
	}
	a.testAdaptorPwmWrite = func() (err error) {
		return errors.New("write error")
	}
	a.testAdaptorServoWrite = func() (err error) {
		return errors.New("write error")
	}
	return NewDirectPinDriver(a, "1")
}

func TestDirectPinDriver(t *testing.T) {
	var ret map[string]interface{}
	var err interface{}

	d := initTestDirectPinDriver()
	gobottest.Assert(t, d.Pin(), "1")
	gobottest.Refute(t, d.Connection(), nil)

	ret = d.Command("DigitalRead")(nil).(map[string]interface{})

	gobottest.Assert(t, ret["val"].(int), 1)
	gobottest.Assert(t, ret["err"], nil)

	err = d.Command("DigitalWrite")(map[string]interface{}{"level": "1"})
	gobottest.Assert(t, err.(error), errors.New("write error"))

	err = d.Command("PwmWrite")(map[string]interface{}{"level": "1"})
	gobottest.Assert(t, err.(error), errors.New("write error"))

	err = d.Command("ServoWrite")(map[string]interface{}{"level": "1"})
	gobottest.Assert(t, err.(error), errors.New("write error"))
}

func TestDirectPinDriverStart(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Assert(t, d.Start(), nil)
}

func TestDirectPinDriverHalt(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Assert(t, d.Halt(), nil)
}

func TestDirectPinDriverOff(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.DigitalWrite(0), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.DigitalWrite(0), nil)
}

func TestDirectPinDriverOn(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.DigitalWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.DigitalWrite(1), nil)
}

func TestDirectPinDriverDigitalWrite(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.DigitalWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.DigitalWrite(1), nil)
}

func TestDirectPinDriverDigitalRead(t *testing.T) {
	d := initTestDirectPinDriver()
	ret, err := d.DigitalRead()
	gobottest.Assert(t, ret, 1)
	gobottest.Assert(t, err, nil)
}

func TestDirectPinDriverPwmWrite(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.PwmWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.PwmWrite(1), nil)
}

func TestDirectPinDriverServoWrite(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.ServoWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.ServoWrite(1), nil)
}

func TestDirectPinDriverDefaultName(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Assert(t, strings.HasPrefix(d.Name(), "Direct"), true)
}

func TestDirectPinDriverSetName(t *testing.T) {
	d := initTestDirectPinDriver()
	d.SetName("mybot")
	gobottest.Assert(t, d.Name(), "mybot")
}
<commit_msg>gpio: increase test coverage for direct pin driver<commit_after>package gpio

import (
	"errors"
	"strings"
	"testing"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/gobottest"
)

var _ gobot.Driver = (*DirectPinDriver)(nil)

func initTestDirectPinDriver() *DirectPinDriver {
	a := newGpioTestAdaptor()
	a.testAdaptorDigitalRead = func() (val int, err error) {
		val = 1
		return
	}
	a.testAdaptorDigitalWrite = func() (err error) {
		return errors.New("write error")
	}
	a.testAdaptorPwmWrite = func() (err error) {
		return errors.New("write error")
	}
	a.testAdaptorServoWrite = func() (err error) {
		return errors.New("write error")
	}
	return NewDirectPinDriver(a, "1")
}

func TestDirectPinDriver(t *testing.T) {
	var ret map[string]interface{}
	var err interface{}

	d := initTestDirectPinDriver()
	gobottest.Assert(t, d.Pin(), "1")
	gobottest.Refute(t, d.Connection(), nil)

	ret = d.Command("DigitalRead")(nil).(map[string]interface{})

	gobottest.Assert(t, ret["val"].(int), 1)
	gobottest.Assert(t, ret["err"], nil)

	err = d.Command("DigitalWrite")(map[string]interface{}{"level": "1"})
	gobottest.Assert(t, err.(error), errors.New("write error"))

	err = d.Command("PwmWrite")(map[string]interface{}{"level": "1"})
	gobottest.Assert(t, err.(error), errors.New("write error"))

	err = d.Command("ServoWrite")(map[string]interface{}{"level": "1"})
	gobottest.Assert(t, err.(error), errors.New("write error"))
}

func TestDirectPinDriverStart(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Assert(t, d.Start(), nil)
}

func TestDirectPinDriverHalt(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Assert(t, d.Halt(), nil)
}

func TestDirectPinDriverOff(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.Off(), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.Off(), nil)
}

func TestDirectPinDriverOn(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.On(), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.On(), nil)
}

func TestDirectPinDriverDigitalWrite(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.DigitalWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.DigitalWrite(1), nil)
}

func TestDirectPinDriverDigitalRead(t *testing.T) {
	d := initTestDirectPinDriver()
	ret, err := d.DigitalRead()
	gobottest.Assert(t, ret, 1)
	gobottest.Assert(t, err, nil)
}

func TestDirectPinDriverPwmWrite(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.PwmWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.PwmWrite(1), nil)
}

func TestDirectPinDriverServoWrite(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Refute(t, d.ServoWrite(1), nil)

	a := newGpioTestAdaptor()
	d = NewDirectPinDriver(a, "1")
	gobottest.Assert(t, d.ServoWrite(1), nil)
}

func TestDirectPinDriverDefaultName(t *testing.T) {
	d := initTestDirectPinDriver()
	gobottest.Assert(t, strings.HasPrefix(d.Name(), "Direct"), true)
}

func TestDirectPinDriverSetName(t *testing.T) {
	d := initTestDirectPinDriver()
	d.SetName("mybot")
	gobottest.Assert(t, d.Name(), "mybot")
}
<|endoftext|>