<commit_before>package ipcc

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"net"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/abh/geoip"
)

var g4 *geoip.GeoIP
var g6 *geoip.GeoIP

func init() {
	var err error

	g4, err = geoip.Open("/usr/share/GeoIP/GeoIP.dat")
	if err != nil {
		fmt.Println("can't open GeoIP.dat")
	}

	g6, err = geoip.Open("/usr/share/GeoIP/GeoIPv6.dat")
	if err != nil {
		fmt.Println("can't open GeoIPv6.dat")
	}

	rand.Seed(time.Now().UnixNano() * int64(os.Getpid()))
}

const N = 10000

func TestMatchGeoIP(t *testing.T) {
	if g4 == nil {
		t.Skip("no geoip data")
	}
	matches := 0
	hits := 0
	for i := 0; i < N; i++ {
		ips := fmt.Sprintf("%d.%d.%d.%d", rand.Int31n(256), rand.Int31n(256), rand.Int31n(256), rand.Int31n(256))
		ip := net.ParseIP(ips)
		if ip == nil {
			panic("can't parse ip")
		}

		ccn := Lookup(ip)
		ccg, _ := g4.GetCountry(ips)
		ccg = strings.ToLower(ccg)

		if ccn == ccg {
			matches++
			if ccn != "" {
				hits++
			}
		}
	}
	r := float32(matches) / N
	t.Log(r, matches, hits)
	if r < 0.9 {
		t.Error("< 90% matches with geoip db")
	}
}

func TestMatchGeoIPv6(t *testing.T) {
	if g6 == nil {
		t.Skip("no geoip data")
	}
	matches := 0
	hits := 0
	for i := 0; i < N; i++ {
		p := []int32{0x2001, 0x2607, 0x2610, 0x2620, 0x2800, 0x2801, 0x2804, 0x2806}
		ips := fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x", p[rand.Int31n(int32(len(p)))], rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000))

		ip := net.ParseIP(ips)
		if ip == nil {
			panic("can't parse ip")
		}

		ccn := Lookup(ip)
		ccg, _ := g6.GetCountry_v6(ips)
		ccg = strings.ToLower(ccg)

		if ccn == ccg {
			matches++
			if ccn != "" {
				hits++
			}
		}
	}
	r := float32(matches) / N
	t.Log(r, matches, hits)
	if r < 0.9 {
		t.Error("< 90% matches with geoip db")
	}
}

func BenchmarkLookupIPv4(b *testing.B) {
	b.ReportAllocs()
	ip := []byte{0, 0, 0, 0}
	for i := 0; i < b.N; i++ {
		r := rand.Uint32()
		ip[0] = byte(r >> 24)
		ip[1] = byte(r >> 16)
		ip[2] = byte(r >> 8)
		ip[3] = byte(r)
		Lookup(ip)
	}
}

func BenchmarkLookupIPv6(b *testing.B) {
	b.ReportAllocs()
	prefixes := []uint16{
		0x0000, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x2001,
		0x2002, 0x2003, 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405,
		0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d,
		0x240e, 0x240f, 0x2600, 0x2601, 0x2602, 0x2603, 0x2604, 0x2605,
		0x2606, 0x2607, 0x2608, 0x2609, 0x260a, 0x260c, 0x260d, 0x260e,
		0x260f, 0x2610, 0x2620, 0x2800, 0x2801, 0x2802, 0x2803, 0x2804,
		0x2805, 0x2806, 0x2807, 0x2808, 0x2a00, 0x2a01, 0x2a02, 0x2a03,
		0x2a04, 0x2a05, 0x2a06, 0x2a07, 0x2a08, 0x2c00, 0x2c08, 0x2c0c,
		0x2c0e, 0x2c0f, 0x4000, 0x6000, 0x8000, 0xa000, 0xc000, 0xe000,
		0xf000, 0xf800, 0xfc00, 0xfe00, 0xfe80, 0xfec0, 0xff00}
	ip := make([]byte, 16)
	for i := 0; i < b.N; i++ {
		pre := prefixes[i%len(prefixes)]
		binary.LittleEndian.PutUint16(ip[0:], pre)
		binary.LittleEndian.PutUint32(ip[2:], rand.Uint32())
		binary.LittleEndian.PutUint32(ip[6:], rand.Uint32())
		binary.LittleEndian.PutUint32(ip[10:], rand.Uint32())
		binary.LittleEndian.PutUint16(ip[14:], uint16(rand.Uint32()))
		Lookup(ip)
	}
}
<commit_msg>move tests to separate package<commit_after>package ipcc_test

import (
	"encoding/binary"
	"fmt"
	"math/rand"
	"net"
	"os"
	"strings"
	"testing"
	"time"

	"github.com/abh/geoip"
	"github.com/ebfe/ipcc"
)

var g4 *geoip.GeoIP
var g6 *geoip.GeoIP

func init() {
	var err error

	g4, err = geoip.Open("/usr/share/GeoIP/GeoIP.dat")
	if err != nil {
		fmt.Println("can't open GeoIP.dat")
	}

	g6, err = geoip.Open("/usr/share/GeoIP/GeoIPv6.dat")
	if err != nil {
		fmt.Println("can't open GeoIPv6.dat")
	}

	rand.Seed(time.Now().UnixNano() * int64(os.Getpid()))
}

const N = 10000

func TestMatchGeoIP(t *testing.T) {
	if g4 == nil {
		t.Skip("no geoip data")
	}
	matches := 0
	hits := 0
	for i := 0; i < N; i++ {
		ips := fmt.Sprintf("%d.%d.%d.%d", rand.Int31n(256), rand.Int31n(256), rand.Int31n(256), rand.Int31n(256))
		ip := net.ParseIP(ips)
		if ip == nil {
			panic("can't parse ip")
		}

		ccn := ipcc.Lookup(ip)
		ccg, _ := g4.GetCountry(ips)
		ccg = strings.ToLower(ccg)

		if ccn == ccg {
			matches++
			if ccn != "" {
				hits++
			}
		}
	}
	r := float32(matches) / N
	t.Log(r, matches, hits)
	if r < 0.9 {
		t.Error("< 90% matches with geoip db")
	}
}

func TestMatchGeoIPv6(t *testing.T) {
	if g6 == nil {
		t.Skip("no geoip data")
	}
	matches := 0
	hits := 0
	for i := 0; i < N; i++ {
		p := []int32{0x2001, 0x2607, 0x2610, 0x2620, 0x2800, 0x2801, 0x2804, 0x2806}
		ips := fmt.Sprintf("%x:%x:%x:%x:%x:%x:%x:%x", p[rand.Int31n(int32(len(p)))], rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000), rand.Int31n(0x10000))

		ip := net.ParseIP(ips)
		if ip == nil {
			panic("can't parse ip")
		}

		ccn := ipcc.Lookup(ip)
		ccg, _ := g6.GetCountry_v6(ips)
		ccg = strings.ToLower(ccg)

		if ccn == ccg {
			matches++
			if ccn != "" {
				hits++
			}
		}
	}
	r := float32(matches) / N
	t.Log(r, matches, hits)
	if r < 0.9 {
		t.Error("< 90% matches with geoip db")
	}
}

func BenchmarkLookupIPv4(b *testing.B) {
	b.ReportAllocs()
	ip := []byte{0, 0, 0, 0}
	for i := 0; i < b.N; i++ {
		r := rand.Uint32()
		ip[0] = byte(r >> 24)
		ip[1] = byte(r >> 16)
		ip[2] = byte(r >> 8)
		ip[3] = byte(r)
		ipcc.Lookup(ip)
	}
}

func BenchmarkLookupIPv6(b *testing.B) {
	b.ReportAllocs()
	prefixes := []uint16{
		0x0000, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x2001,
		0x2002, 0x2003, 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405,
		0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d,
		0x240e, 0x240f, 0x2600, 0x2601, 0x2602, 0x2603, 0x2604, 0x2605,
		0x2606, 0x2607, 0x2608, 0x2609, 0x260a, 0x260c, 0x260d, 0x260e,
		0x260f, 0x2610, 0x2620, 0x2800, 0x2801, 0x2802, 0x2803, 0x2804,
		0x2805, 0x2806, 0x2807, 0x2808, 0x2a00, 0x2a01, 0x2a02, 0x2a03,
		0x2a04, 0x2a05, 0x2a06, 0x2a07, 0x2a08, 0x2c00, 0x2c08, 0x2c0c,
		0x2c0e, 0x2c0f, 0x4000, 0x6000, 0x8000, 0xa000, 0xc000, 0xe000,
		0xf000, 0xf800, 0xfc00, 0xfe00, 0xfe80, 0xfec0, 0xff00}
	ip := make([]byte, 16)
	for i := 0; i < b.N; i++ {
		pre := prefixes[i%len(prefixes)]
		binary.LittleEndian.PutUint16(ip[0:], pre)
		binary.LittleEndian.PutUint32(ip[2:], rand.Uint32())
		binary.LittleEndian.PutUint32(ip[6:], rand.Uint32())
		binary.LittleEndian.PutUint32(ip[10:], rand.Uint32())
		binary.LittleEndian.PutUint16(ip[14:], uint16(rand.Uint32()))
		ipcc.Lookup(ip)
	}
}
<|endoftext|>


<commit_before>package service

import (
	"io/ioutil"
	"os"
	"runtime"
	"strings"

	"github.com/juju/errors"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/version"
)

// DiscoverService returns an interface to a service apropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
	initName, err := discoverLocalInitSystem()
	if errors.IsNotFound(err) {
		// Fall back to checking the juju version.
		versionInitName, ok := VersionInitSystem(version.Current)
		if !ok {
			return nil, errors.NotFoundf("init system on local host")
		}
		initName = versionInitName
	}
	if err != nil {
		return nil, errors.Trace(err)
	}

	service, err := NewService(name, conf, initName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return service, nil
}

// VersionInitSystem returns an init system name based on the provided
// version info. If one cannot be identified then false if returned
// for the second return value.
func VersionInitSystem(vers version.Binary) (string, bool) {
	switch vers.OS {
	case version.Windows:
		return InitSystemWindows, true
	case version.Ubuntu:
		switch vers.Series {
		case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
			return InitSystemUpstart, true
		default:
			// vivid and later
			return InitSystemSystemd, true
		}
		// TODO(ericsnow) Support other OSes, like version.CentOS.
	default:
		return "", false
	}
}

type initSystem struct {
	executable string
	name       string
}

var linuxExecutables = []initSystem{
	// Note that some systems link /sbin/init to whatever init system
	// is supported, so in the future we may need some other way to
	// identify upstart uniquely.
	{"/sbin/init", InitSystemUpstart},
	{"/sbin/upstart", InitSystemUpstart},
	{"/sbin/systemd", InitSystemSystemd},
	{"/bin/systemd", InitSystemSystemd},
	{"/lib/systemd/systemd", InitSystemSystemd},
}

func identifyInitSystem(executable string) (string, bool) {
	for _, initSystem := range linuxExecutables {
		if executable == initSystem.executable {
			return initSystem.name, true
		}
	}
	return "", false
}

func discoverLocalInitSystem() (string, error) {
	if runtime.GOOS == "windows" {
		return InitSystemWindows, nil
	}

	data, err := ioutil.ReadFile("/proc/1/cmdline")
	if os.IsNotExist(err) {
		return "", errors.NotFoundf("init system")
	}
	if err != nil {
		return "", errors.Trace(err)
	}
	executable := strings.Fields(string(data))[0]

	initName, ok := identifyInitSystem(executable)
	if !ok {
		return "", errors.NotFoundf("init system (%s)", executable)
	}
	return initName, nil
}

// TODO(ericsnow) Is it too much to cat once for each executable?
const initSystemTest = `[[ "$(cat /proc/1/cmdline | awk '{print $1}')" == "%s" ]]`
<commit_msg>Clean up the executable path.<commit_after>package service

import (
	"io/ioutil"
	"os"
	"runtime"
	"strings"

	"github.com/juju/errors"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/version"
)

// DiscoverService returns an interface to a service apropriate
// for the current system
func DiscoverService(name string, conf common.Conf) (Service, error) {
	initName, err := discoverLocalInitSystem()
	if errors.IsNotFound(err) {
		// Fall back to checking the juju version.
		versionInitName, ok := VersionInitSystem(version.Current)
		if !ok {
			return nil, errors.NotFoundf("init system on local host")
		}
		initName = versionInitName
	}
	if err != nil {
		return nil, errors.Trace(err)
	}

	service, err := NewService(name, conf, initName)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return service, nil
}

// VersionInitSystem returns an init system name based on the provided
// version info. If one cannot be identified then false if returned
// for the second return value.
func VersionInitSystem(vers version.Binary) (string, bool) {
	switch vers.OS {
	case version.Windows:
		return InitSystemWindows, true
	case version.Ubuntu:
		switch vers.Series {
		case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
			return InitSystemUpstart, true
		default:
			// vivid and later
			return InitSystemSystemd, true
		}
		// TODO(ericsnow) Support other OSes, like version.CentOS.
	default:
		return "", false
	}
}

type initSystem struct {
	executable string
	name       string
}

var linuxExecutables = []initSystem{
	// Note that some systems link /sbin/init to whatever init system
	// is supported, so in the future we may need some other way to
	// identify upstart uniquely.
	{"/sbin/init", InitSystemUpstart},
	{"/sbin/upstart", InitSystemUpstart},
	{"/sbin/systemd", InitSystemSystemd},
	{"/bin/systemd", InitSystemSystemd},
	{"/lib/systemd/systemd", InitSystemSystemd},
}

func identifyInitSystem(executable string) (string, bool) {
	for _, initSystem := range linuxExecutables {
		if executable == initSystem.executable {
			return initSystem.name, true
		}
	}
	return "", false
}

func discoverLocalInitSystem() (string, error) {
	if runtime.GOOS == "windows" {
		return InitSystemWindows, nil
	}

	data, err := ioutil.ReadFile("/proc/1/cmdline")
	if os.IsNotExist(err) {
		return "", errors.NotFoundf("init system")
	}
	if err != nil {
		return "", errors.Trace(err)
	}
	out := strings.Trim(strings.TrimSpace(string(data)), "\x00")
	executable := strings.Fields(out)[0]

	initName, ok := identifyInitSystem(executable)
	if !ok {
		return "", errors.NotFoundf("init system (based on %s)", executable)
	}
	return initName, nil
}

// TODO(ericsnow) Is it too much to cat once for each executable?
const initSystemTest = `[[ "$(cat /proc/1/cmdline | awk '{print $1}')" == "%s" ]]`
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"summercat.com/irc"
)

// User holds information about a user. It may be remote or local.
type User struct {
	DisplayNick string
	HopCount    int
	NickTS      int64
	Modes       map[byte]struct{}
	Username    string
	Hostname    string
	IP          string
	UID         TS6UID
	RealName    string

	// Channel name (canonicalized) to Channel.
	Channels map[string]*Channel

	// LocalUser set if this is a local user.
	LocalUser *LocalUser

	// Link set if this is a remote user.
	// This is the server we heard about the user from. It is not necessarily the
	// server they are on. It could be on a server linked to the one we are
	// linked to.
	Link *LocalServer
}

func (u *User) String() string {
	return fmt.Sprintf("%s: %s", u.UID, u.nickUhost())
}
func (u *User) nickUhost() string {
	return fmt.Sprintf("%s!~%s@%s", u.DisplayNick, u.Username, u.Hostname)
}

func (u *User) isOperator() bool {
	_, exists := u.Modes['o']
	return exists
}

func (u *User) onChannel(channel *Channel) bool {
	_, exists := u.Channels[channel.Name]
	return exists
}

func (u *User) modesString() string {
	s := "+"
	for m := range u.Modes {
		s += string(m)
	}
	return s
}

func (u *User) isLocal() bool {
	return u.LocalUser != nil
}

func (u *User) isRemote() bool {
	return !u.isLocal()
}

func (u *User) messageUser(to *User, command string, params []string) {
	if u.isLocal() {
		to.LocalUser.maybeQueueMessage(irc.Message{
			Prefix:  u.nickUhost(),
			Command: command,
			Params:  params,
		})
		return
	}

	to.Link.maybeQueueMessage(irc.Message{
		Prefix:  string(u.UID),
		Command: command,
		Params:  params,
	})
}
<commit_msg>ircd: Part of last commit<commit_after>package main

import "fmt"

// User holds information about a user. It may be remote or local.
type User struct {
	DisplayNick string
	HopCount    int
	NickTS      int64
	Modes       map[byte]struct{}
	Username    string
	Hostname    string
	IP          string
	UID         TS6UID
	RealName    string

	// Channel name (canonicalized) to Channel.
	Channels map[string]*Channel

	// LocalUser set if this is a local user.
	LocalUser *LocalUser

	// Link set if this is a remote user.
	// This is the server we heard about the user from. It is not necessarily the
	// server they are on. It could be on a server linked to the one we are
	// linked to.
	Link *LocalServer
}

func (u *User) String() string {
	return fmt.Sprintf("%s: %s", u.UID, u.nickUhost())
}
func (u *User) nickUhost() string {
	return fmt.Sprintf("%s!~%s@%s", u.DisplayNick, u.Username, u.Hostname)
}

func (u *User) isOperator() bool {
	_, exists := u.Modes['o']
	return exists
}

func (u *User) onChannel(channel *Channel) bool {
	_, exists := u.Channels[channel.Name]
	return exists
}

func (u *User) modesString() string {
	s := "+"
	for m := range u.Modes {
		s += string(m)
	}
	return s
}

func (u *User) isLocal() bool {
	return u.LocalUser != nil
}

func (u *User) isRemote() bool {
	return !u.isLocal()
}
<|endoftext|>


<commit_before>package node

// xlattice_go/node/base_node.go

import (
	"code.google.com/p/go.crypto/ssh"
	"crypto/rsa"
	"encoding/hex"
	"errors"
	"fmt"
	xc "github.com/jddixon/xlattice_go/crypto"
	xo "github.com/jddixon/xlattice_go/overlay"
	"strings"
)

var _ = fmt.Print

/**
 * Basic abstraction underlying Peer and Node
 */

type BaseNode struct {
	name        string // convenience for testing
	nodeID      *NodeID
	commsPubKey *rsa.PublicKey
	sigPubKey   *rsa.PublicKey
	overlays    []xo.OverlayI
}

func NewNewBaseNode(name string, id *NodeID) (*BaseNode, error) {
	return NewBaseNode(name, id, nil, nil, nil)
}

func NewBaseNode(name string, id *NodeID,
	ck *rsa.PublicKey, sk *rsa.PublicKey,
	o []xo.OverlayI) (p *BaseNode, err error) {

	// IDENTITY /////////////////////////////////////////////////////
	if id == nil {
		err := errors.New("IllegalArgument: nil NodeID")
		return nil, err
	}
	nodeID, err := (*id).Clone()
	if err != nil {
		return
	}
	commsPubKey := ck
	sigPubKey := sk
	var overlays []xo.OverlayI // an empty slice
	if o != nil {
		count := len(o)
		for i := 0; i < count; i++ {
			overlays = append(overlays, o[i])
		}
	} // FOO
	p = new(BaseNode)
	p.name = name
	p.nodeID = nodeID // the clone
	p.commsPubKey = commsPubKey
	p.sigPubKey = sigPubKey
	p.overlays = overlays
	return p, nil
}
func (p *BaseNode) GetName() string {
	return p.name
}
func (p *BaseNode) GetNodeID() *NodeID {
	return p.nodeID
}
func (p *BaseNode) GetCommsPublicKey() *rsa.PublicKey {
	return p.commsPubKey
}
func (p *BaseNode) GetSSHCommsPublicKey() string {
	out := ssh.MarshalAuthorizedKey(p.commsPubKey)
	return string(out)
}

func (p *BaseNode) GetSigPublicKey() *rsa.PublicKey {
	return p.sigPubKey
}

// OVERLAYS /////////////////////////////////////////////////////////
//func (p *BaseNode) addOverlayI(o xo.OverlayI) error {
//	if o == nil {
//		return errors.New("IllegalArgument: nil OverlayI")
//	}
//	p.overlays = append(p.overlays, o)
//	return nil
//} // FOO
func (n *BaseNode) AddOverlay(o xo.OverlayI) (ndx int, err error) {
	ndx = -1
	if o == nil {
		err = errors.New("IllegalArgument: nil Overlay")
	} else {
		for i := 0; i < len(n.overlays); i++ {
			if n.overlays[i].Equal(o) {
				ndx = i
				break
			}
		}
		if ndx == -1 {
			n.overlays = append(n.overlays, o)
			ndx = len(n.overlays) - 1
		}
	}
	return
} // FOO

// Return a count of the number of overlays.
func (p *BaseNode) SizeOverlays() int {
	return len(p.overlays)
}

func (p *BaseNode) GetOverlay(n int) xo.OverlayI {
	return p.overlays[n]
}

// EQUAL ////////////////////////////////////////////////////////////
func (p *BaseNode) Equal(any interface{}) bool {
	if any == p {
		return true
	}
	if any == nil {
		return false
	}
	switch v := any.(type) {
	case BaseNode:
		_ = v
	default:
		return false
	}
	other := any.(Peer) // type assertion
	// THINK ABOUT publicKey.equals(any.publicKey)
	if p.nodeID == other.nodeID {
		return true
	}
	if p.nodeID.Length() != other.nodeID.Length() {
		return false
	}
	myVal := p.nodeID.Value()
	otherVal := other.nodeID.Value()
	for i := 0; i < p.nodeID.Length(); i++ {
		if myVal[i] != otherVal[i] {
			return false
		}
	}
	return false
}

// SERIALIZATION ////////////////////////////////////////////////////

func addStringlet(slice *[]string, s string) {
	*slice = append(*slice, s)
}
func (p *BaseNode) Strings() []string {
	ckSSH, err := xc.RSAPubKeyToDisk(p.commsPubKey)
	if err != nil {
		panic(err)
	}
	skSSH, err := xc.RSAPubKeyToDisk(p.sigPubKey)
	if err != nil {
		panic(err)
	}

	var s []string
	addStringlet(&s, fmt.Sprintf("name: %s", p.name))
	addStringlet(&s, fmt.Sprintf("nodeID: %s", p.nodeID.String()))
	addStringlet(&s, fmt.Sprintf("commsPubKey: %s", ckSSH))
	addStringlet(&s, fmt.Sprintf("sigPubKey: %s", skSSH))
	addStringlet(&s, fmt.Sprintf("overlays {"))
	for i := 0; i < len(p.overlays); i++ {
		addStringlet(&s, fmt.Sprintf("    %s", p.overlays[i].String()))
	}
	addStringlet(&s, fmt.Sprintf("}"))
	return s
}

// DESERIALIZATION //////////////////////////////////////////////////

var (
	NotABaseNode = errors.New("not a serialized BaseNode - missing bits")
)

// Return the next non-blank line in the slice of strings
func nextLine(lines *[]string) string {
	if lines != nil {
		for len(*lines) > 0 {
			s := strings.TrimSpace((*lines)[0])
			*lines = (*lines)[1:]
			if s != "" {
				return s
			}
		}
	}
	return ""
}

// Parse a serialized BaseNode, ignoring blank lines and leading
// and trailing whitespace
func ParseBaseNode(data string) (bn *BaseNode, rest []string, err error) {
	ss := strings.Split(data, "\n") // yields a slice of strings
	var (
		name        string
		nodeID      *NodeID
		commsPubKey *rsa.PublicKey
		sigPubKey   *rsa.PublicKey
		overlays    []xo.OverlayI
	)
	s := nextLine(&ss)
	if strings.HasPrefix(s, "name: ") {
		name = s[6:]
	} else {
		err = NotABaseNode
	}
	if err == nil {
		s = nextLine(&ss)
		if strings.HasPrefix(s, "nodeID: ") {
			var val []byte
			val, err = hex.DecodeString(s[7:])
			if err == nil {
				nodeID, err = NewNodeID(val)
			}
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if strings.HasPrefix(s, "commsPubKey: ") {
			// XXX we do not verify that the next line is empty
			ckSSH := []byte(s[13:] + "\n")
			commsPubKey, err = xc.RSAPubKeyFromDisk(ckSSH)
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if strings.HasPrefix(s, "SigPubKey: ") {
			skSSH := []byte(s[12:] + "\n")
			commsPubKey, err = xc.RSAPubKeyFromDisk(skSSH)
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if s == "overlays {" {
			for {
				s = nextLine(&ss)
				if s == "" { // end of strings
					err = NotABaseNode
					break
				} else if s == "}" {
					prepend := []string{s}
					ss = append(prepend, ss...)
					break
				} else if strings.HasPrefix("IPOverlay: ", s) {
					var ar *xo.AddrRange
					ar, err = xo.NewCIDRAddrRange(s[11:])
					if err != nil {
						break
					}
					var o xo.OverlayI
					// XXX WE DON'T HAVE ITS NAME OR COST!
					o, err = xo.NewIPOverlay("", ar, "ip", 1.0)
					if err != nil {
						break
					}
					overlays = append(overlays, o)
				} else {
					// we can only handle IP overlays
					fmt.Printf("not a recognized IP overlay: %s\n", s)
					err = NotABaseNode
					break
				}
			}
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if s != "}" {
			err = NotABaseNode
		}
	}
	if err == nil {
		var bn = BaseNode{name, nodeID, commsPubKey, sigPubKey, overlays}
		return &bn, ss, nil
	} else {
		return nil, nil, err
	}
}
<commit_msg>adding overlay parser and test<commit_after>package node

// xlattice_go/node/base_node.go

import (
	"code.google.com/p/go.crypto/ssh"
	"crypto/rsa"
	"encoding/hex"
	"errors"
	"fmt"
	xc "github.com/jddixon/xlattice_go/crypto"
	xo "github.com/jddixon/xlattice_go/overlay"
	"strings"
)

var _ = fmt.Print

/**
 * Basic abstraction underlying Peer and Node
 */

type BaseNode struct {
	name        string // convenience for testing
	nodeID      *NodeID
	commsPubKey *rsa.PublicKey
	sigPubKey   *rsa.PublicKey
	overlays    []xo.OverlayI
}

func NewNewBaseNode(name string, id *NodeID) (*BaseNode, error) {
	return NewBaseNode(name, id, nil, nil, nil)
}

func NewBaseNode(name string, id *NodeID,
	ck *rsa.PublicKey, sk *rsa.PublicKey,
	o []xo.OverlayI) (p *BaseNode, err error) {

	// IDENTITY /////////////////////////////////////////////////////
	if id == nil {
		err := errors.New("IllegalArgument: nil NodeID")
		return nil, err
	}
	nodeID, err := (*id).Clone()
	if err != nil {
		return
	}
	commsPubKey := ck
	sigPubKey := sk
	var overlays []xo.OverlayI // an empty slice
	if o != nil {
		count := len(o)
		for i := 0; i < count; i++ {
			overlays = append(overlays, o[i])
		}
	} // FOO
	p = new(BaseNode)
	p.name = name
	p.nodeID = nodeID // the clone
	p.commsPubKey = commsPubKey
	p.sigPubKey = sigPubKey
	p.overlays = overlays
	return p, nil
}
func (p *BaseNode) GetName() string {
	return p.name
}
func (p *BaseNode) GetNodeID() *NodeID {
	return p.nodeID
}
func (p *BaseNode) GetCommsPublicKey() *rsa.PublicKey {
	return p.commsPubKey
}
func (p *BaseNode) GetSSHCommsPublicKey() string {
	out := ssh.MarshalAuthorizedKey(p.commsPubKey)
	return string(out)
}

func (p *BaseNode) GetSigPublicKey() *rsa.PublicKey {
	return p.sigPubKey
}

// OVERLAYS /////////////////////////////////////////////////////////
//func (p *BaseNode) addOverlayI(o xo.OverlayI) error {
//	if o == nil {
//		return errors.New("IllegalArgument: nil OverlayI")
//	}
//	p.overlays = append(p.overlays, o)
//	return nil
//} // FOO
func (n *BaseNode) AddOverlay(o xo.OverlayI) (ndx int, err error) {
	ndx = -1
	if o == nil {
		err = errors.New("IllegalArgument: nil Overlay")
	} else {
		for i := 0; i < len(n.overlays); i++ {
			if n.overlays[i].Equal(o) {
				ndx = i
				break
			}
		}
		if ndx == -1 {
			n.overlays = append(n.overlays, o)
			ndx = len(n.overlays) - 1
		}
	}
	return
} // FOO

// Return a count of the number of overlays.
func (p *BaseNode) SizeOverlays() int {
	return len(p.overlays)
}

func (p *BaseNode) GetOverlay(n int) xo.OverlayI {
	return p.overlays[n]
}

// EQUAL ////////////////////////////////////////////////////////////
func (p *BaseNode) Equal(any interface{}) bool {
	if any == p {
		return true
	}
	if any == nil {
		return false
	}
	switch v := any.(type) {
	case BaseNode:
		_ = v
	default:
		return false
	}
	other := any.(Peer) // type assertion
	// THINK ABOUT publicKey.equals(any.publicKey)
	if p.nodeID == other.nodeID {
		return true
	}
	if p.nodeID.Length() != other.nodeID.Length() {
		return false
	}
	myVal := p.nodeID.Value()
	otherVal := other.nodeID.Value()
	for i := 0; i < p.nodeID.Length(); i++ {
		if myVal[i] != otherVal[i] {
			return false
		}
	}
	return false
}

// SERIALIZATION ////////////////////////////////////////////////////

func addStringlet(slice *[]string, s string) {
	*slice = append(*slice, s)
}
func (p *BaseNode) Strings() []string {
	ckSSH, err := xc.RSAPubKeyToDisk(p.commsPubKey)
	if err != nil {
		panic(err)
	}
	skSSH, err := xc.RSAPubKeyToDisk(p.sigPubKey)
	if err != nil {
		panic(err)
	}

	var s []string
	addStringlet(&s, fmt.Sprintf("name: %s", p.name))
	addStringlet(&s, fmt.Sprintf("nodeID: %s", p.nodeID.String()))
	addStringlet(&s, fmt.Sprintf("commsPubKey: %s", ckSSH))
	addStringlet(&s, fmt.Sprintf("sigPubKey: %s", skSSH))
	addStringlet(&s, fmt.Sprintf("overlays {"))
	for i := 0; i < len(p.overlays); i++ {
		addStringlet(&s, fmt.Sprintf("    %s", p.overlays[i].String()))
	}
	addStringlet(&s, fmt.Sprintf("}"))
	return s
}

// DESERIALIZATION //////////////////////////////////////////////////

var (
	NotABaseNode = errors.New("not a serialized BaseNode - missing bits")
)

// Return the next non-blank line in the slice of strings
func nextLine(lines *[]string) string {
	if lines != nil {
		for len(*lines) > 0 {
			s := strings.TrimSpace((*lines)[0])
			*lines = (*lines)[1:]
			if s != "" {
				return s
			}
		}
	}
	return ""
}

// Parse a serialized BaseNode, ignoring blank lines and leading
// and trailing whitespace
func ParseBaseNode(data string) (bn *BaseNode, rest []string, err error) {
	ss := strings.Split(data, "\n") // yields a slice of strings
	var (
		name        string
		nodeID      *NodeID
		commsPubKey *rsa.PublicKey
		sigPubKey   *rsa.PublicKey
		overlays    []xo.OverlayI
	)
	s := nextLine(&ss)
	if strings.HasPrefix(s, "name: ") {
		name = s[6:]
	} else {
		err = NotABaseNode
	}
	if err == nil {
		s = nextLine(&ss)
		if strings.HasPrefix(s, "nodeID: ") {
			var val []byte
			val, err = hex.DecodeString(s[7:])
			if err == nil {
				nodeID, err = NewNodeID(val)
			}
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if strings.HasPrefix(s, "commsPubKey: ") {
			// XXX we do not verify that the next line is empty
			ckSSH := []byte(s[13:] + "\n")
			commsPubKey, err = xc.RSAPubKeyFromDisk(ckSSH)
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if strings.HasPrefix(s, "SigPubKey: ") {
			skSSH := []byte(s[12:] + "\n")
			commsPubKey, err = xc.RSAPubKeyFromDisk(skSSH)
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if s == "overlays {" {
			for {
				s = nextLine(&ss)
				if s == "" { // end of strings
					err = NotABaseNode
					break
				} else if s == "}" {
					prepend := []string{s}
					ss = append(prepend, ss...)
					break
				} else if strings.HasPrefix("IPOverlay: ", s) {
					var ar *xo.AddrRange
					ar, err = xo.NewCIDRAddrRange(s[11:])
					if err != nil {
						break
					}
					var o xo.OverlayI
					// XXX WE DON'T HAVE ITS NAME OR COST!
					o, err = xo.NewIPOverlay("", ar, "ip", 1.0)
					if err != nil {
						break
					}	// GEEP
					overlays = append(overlays, o)
				} else {
					// we can only handle IP overlays
					fmt.Printf("not a recognized IP overlay: %s\n", s)
					err = NotABaseNode
					break
				}
			}
		} else {
			err = NotABaseNode
		}
	}
	if err == nil {
		s = nextLine(&ss)
		if s != "}" {
			err = NotABaseNode
		}
	}
	if err == nil {
		var bn = BaseNode{name, nodeID, commsPubKey, sigPubKey, overlays}
		return &bn, ss, nil
	} else {
		return nil, nil, err
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"encoding/xml"
	"flag"
	irc "github.com/fluffle/goirc/client"
	_ "github.com/go-sql-driver/mysql"
	"html"
	"io/ioutil"
	"log"
	"net/http"
	"strings"
)

type Config struct {
	Channel string
	DBConn  string
	BotName string
}

var config Config

func sendUrl(channel, url string, conn *irc.Conn) {
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	respbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	stringbody := string(respbody)
	titlestart := strings.Index(stringbody, "<title>")
	titleend := strings.Index(stringbody, "</title>")
	if titlestart != -1 && titlestart != -1 {
		title := string(respbody[titlestart+7 : titleend])
		if strings.TrimSpace(title) != "" {
			title = "Title: " + html.UnescapeString(title)
			conn.Privmsg(channel, title)
		}
	}
}

func handleMessage(conn *irc.Conn, line *irc.Line) {
	urllist := []string{}
	numlinks := 0
NextWord:
	for _, word := range strings.Split(line.Args[1], " ") {
		word = strings.TrimSpace(word)
		if strings.HasPrefix(word, "http") {
			for _, subUrl := range urllist {
				if subUrl == word {
					continue NextWord
				}
			}
			numlinks++
			if numlinks > 3 {
				break
			}
			urllist = append(urllist, word)
			go sendUrl(line.Args[0], word, conn)
		}

	}
	db, err := sql.Open("mysql", config.DBConn)
	if err != nil {
		log.Println(err)
	}
	defer db.Close()
	_, err = db.Exec("insert into messages (Nick, Ident, Host, Src, Cmd, Channel, Message, Time) values (?, ?, ?, ?, ?, ?, ?, ?)", line.Nick, line.Ident, line.Host, line.Src, line.Cmd, line.Args[0], line.Args[1], line.Time)
	if err != nil {
		log.Println(err)
	}
}

func main() {
	flag.Parse()

	xmlFile, err := ioutil.ReadFile("config.xml")
	if err != nil {
		log.Fatal(err)
	}
	xml.Unmarshal(xmlFile, &config)

	log.Printf("Joining channel %s", config.Channel)

	c := irc.SimpleClient(config.BotName)

	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			conn.Join(config.Channel)
			log.Println("Connected!")
		})

	quit := make(chan bool)

	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	c.AddHandler("PRIVMSG", handleMessage)

	if err := c.Connect("irc.freenode.net"); err != nil {
		log.Fatalln("Connection error: %s\n", err)
	}

	<-quit
}
<commit_msg>Regex for url matching<commit_after>package main

import (
	"database/sql"
	"encoding/xml"
	"flag"
	irc "github.com/fluffle/goirc/client"
	_ "github.com/go-sql-driver/mysql"
	"html"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strings"
)

type Config struct {
	Channel string
	DBConn  string
	BotName string
}

var config Config

var urlRegex, regexErr = regexp.Compile(`(?i)\b((?:https?://|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s` + "`" + `!()\[\]{};:'".,<>?«»“”‘’]))`)

func sendUrl(channel, url string, conn *irc.Conn) {
	if !strings.HasPrefix(url, "http://") {
		url = "http://" + url
	}
	resp, err := http.Get(url)
	if err != nil {
		return
	}
	respbody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	stringbody := string(respbody)
	titlestart := strings.Index(stringbody, "<title>")
	titleend := strings.Index(stringbody, "</title>")
	if titlestart != -1 && titlestart != -1 {
		title := string(respbody[titlestart+7 : titleend])
		if strings.TrimSpace(title) != "" {
			title = "Title: " + html.UnescapeString(title) + " (" + url + ")"
			log.Println(title)
			conn.Privmsg(channel, strings.Replace(title, "\n", " ", -1))
		}
	}
}

func handleMessage(conn *irc.Conn, line *irc.Line) {
	urllist := []string{}
	numlinks := 0
NextWord:
	for _, word := range strings.Split(line.Args[1], " ") {
		word = strings.TrimSpace(word)
		if urlRegex.MatchString(word) {
			for _, subUrl := range urllist {
				if subUrl == word {
					continue NextWord
				}
			}
			numlinks++
			if numlinks > 3 {
				break
			}
			urllist = append(urllist, word)
			go sendUrl(line.Args[0], word, conn)
		}

	}
	db, err := sql.Open("mysql", config.DBConn)
	if err != nil {
		log.Println(err)
	}
	defer db.Close()
	_, err = db.Exec("insert into messages (Nick, Ident, Host, Src, Cmd, Channel, Message, Time) values (?, ?, ?, ?, ?, ?, ?, ?)", line.Nick, line.Ident, line.Host, line.Src, line.Cmd, line.Args[0], line.Args[1], line.Time)
	if err != nil {
		log.Println(err)
	}
}

func init() {
	log.Println("Starting sadbot")

	flag.Parse()

	if regexErr != nil {
		log.Panic(regexErr)
	}

	xmlFile, err := ioutil.ReadFile("config.xml")
	if err != nil {
		log.Fatal(err)
	}
	xml.Unmarshal(xmlFile, &config)
}

func main() {
	log.Printf("Joining channel %s", config.Channel)

	c := irc.SimpleClient(config.BotName)

	c.AddHandler(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			conn.Join(config.Channel)
			log.Println("Connected!")
		})

	quit := make(chan bool)

	c.AddHandler(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) { quit <- true })

	c.AddHandler("PRIVMSG", handleMessage)

	if err := c.Connect("irc.freenode.net"); err != nil {
		log.Fatalln("Connection error: %s\n", err)
	}

	<-quit
}
<|endoftext|>


<commit_before>package sudoku

import (
	"log"
	"testing"
)

func TestSubsetIndexes(t *testing.T) {
	result := subsetIndexes(3, 1)
	expectedResult := [][]int{[]int{0}, []int{1}, []int{2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(3, 2)
	expectedResult = [][]int{[]int{0, 1}, []int{0, 2}, []int{1, 2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(5, 3)
	expectedResult = [][]int{[]int{0, 1, 2}, []int{0, 1, 3}, []int{0, 1, 4}, []int{0, 2, 3}, []int{0, 2, 4}, []int{0, 3, 4}, []int{1, 2, 3}, []int{1, 2, 4}, []int{1, 3, 4}, []int{2, 3, 4}}
	subsetIndexHelper(t, result, expectedResult)

	if subsetIndexes(1, 2) != nil {
		t.Log("Subset indexes returned a subset where the length is greater than the len")
		t.Fail()
	}

}

func subsetIndexHelper(t *testing.T, result [][]int, expectedResult [][]int) {
	if len(result) != len(expectedResult) {
		t.Log("subset indexes returned wrong number of results for: ", result, " :", expectedResult)
		t.FailNow()
	}
	for i, item := range result {
		if len(item) != len(expectedResult[0]) {
			t.Log("subset indexes returned a result with wrong numbrer of items ", i, " : ", result, " : ", expectedResult)
			t.FailNow()
		}
		for j, value := range item {
			if value != expectedResult[i][j] {
				t.Log("Subset indexes had wrong number at ", i, ",", j, " : ", result, " : ", expectedResult)
				t.Fail()
			}
		}
	}
}

type solveTechniqueMatchMode int

const (
	solveTechniqueMatchModeAll = iota
	solveTechniqueMatchModeAny
)

type solveTechniqueTestHelperOptions struct {
	transpose bool
	//Whether the descriptions of cells are a list of legal possible individual values, or must all match.
	matchMode    solveTechniqueMatchMode
	targetCells  []cellRef
	pointerCells []cellRef
	targetNums   IntSlice
	pointerNums  IntSlice
	targetSame   cellGroupType
	targetGroup  int
	//If description provided, the description MUST match.
	description string
	//If descriptions provided, ONE of the descriptions must match.
	//generally used in conjunction with solveTechniqueMatchModeAny.
	descriptions []string
	debugPrint   bool
}

func humanSolveTechniqueTestHelper(t *testing.T, puzzleName string, techniqueName string, options solveTechniqueTestHelperOptions) {
	//TODO: test for col and block as well
	grid := NewGrid()
	grid.LoadFromFile(puzzlePath(puzzleName))

	if options.transpose {
		grid = grid.transpose()
	}

	solver := techniquesByName[techniqueName]

	if solver == nil {
		t.Fatal("Couldn't find technique object: ", techniqueName)
	}

	steps := solver.Find(grid)

	if len(steps) == 0 {
		t.Fatal(techniqueName, " didn't find a cell it should have.")
	}

	step := steps[0]

	if options.debugPrint {
		log.Println(step)
	}

	if options.matchMode == solveTechniqueMatchModeAll {

		//All must match

		if options.targetCells != nil {
			if !step.TargetCells.sameAsRefs(options.targetCells) {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			if !step.PointerCells.sameAsRefs(options.pointerCells) {
				t.Error(techniqueName, " had the wrong pointer cells: ", step.PointerCells)
			}
		}

		switch options.targetSame {
		case GROUP_ROW:
			if !step.TargetCells.SameRow() || step.TargetCells.Row() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong row :", step.TargetCells.Row())
			}
		case GROUP_BLOCK:
			if !step.TargetCells.SameBlock() || step.TargetCells.Block() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong block :", step.TargetCells.Block())
			}
		case GROUP_COL:
			if !step.TargetCells.SameCol() || step.TargetCells.Col() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong col :", step.TargetCells.Col())
			}
		case GROUP_NONE:
			//Do nothing
		default:
			t.Error("human solve technique helper error: unsupported group type: ", options.targetSame)
		}

		if options.targetNums != nil {
			if !step.TargetNums.SameContentAs(options.targetNums) {
				t.Error(techniqueName, " found the wrong numbers: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			if !step.PointerNums.SameContentAs(options.pointerNums) {
				t.Error(techniqueName, "found the wrong numbers:", step.PointerNums)
			}
		}
	} else if options.matchMode == solveTechniqueMatchModeAny {

		foundMatch := false

		if options.targetCells != nil {
			foundMatch = false
			for _, ref := range options.targetCells {
				for _, cell := range step.TargetCells {
					if ref.Cell(grid) == cell {
						//TODO: break out early
						foundMatch = true
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			t.Error("Pointer cells in match mode any not yet supported.")
		}

		if options.targetSame != GROUP_NONE {
			t.Error("Target Same in match mode any not yet supported.")
		}

		if options.targetNums != nil {
			foundMatch = false
			for _, targetNum := range options.targetNums {
				for _, num := range step.TargetNums {
					if targetNum == num {
						foundMatch = true
						//TODO: break early here.
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target nums: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			t.Error("Pointer nums in match mode any not yet supported.")
		}
	}

	if options.description != "" {
		//Normalize the step so that the description will be stable for the test.
		step.normalize()
		description := solver.Description(step)
		if description != options.description {
			t.Error("Wrong description for ", techniqueName, ". Got:*", description, "* expected: *", options.description, "*")
		}
	} else if options.descriptions != nil {
		foundMatch := false
		step.normalize()
		description := solver.Description(step)
		for _, targetDescription := range options.descriptions {
			if description == targetDescription {
				foundMatch = true
			}
		}
		if !foundMatch {
			t.Error("No descriptions matched for ", techniqueName, ". Got:*", description)
		}
	}

	//TODO: we should do exhaustive testing of SolveStep application. We used to test it here, but as long as targetCells and targetNums are correct it should be fine.

	grid.Done()
}
<commit_msg>Added support for pointerNums in Or Mode of test helper<commit_after>package sudoku

import (
	"log"
	"testing"
)

func TestSubsetIndexes(t *testing.T) {
	result := subsetIndexes(3, 1)
	expectedResult := [][]int{[]int{0}, []int{1}, []int{2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(3, 2)
	expectedResult = [][]int{[]int{0, 1}, []int{0, 2}, []int{1, 2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(5, 3)
	expectedResult = [][]int{[]int{0, 1, 2}, []int{0, 1, 3}, []int{0, 1, 4}, []int{0, 2, 3}, []int{0, 2, 4}, []int{0, 3, 4}, []int{1, 2, 3}, []int{1, 2, 4}, []int{1, 3, 4}, []int{2, 3, 4}}
	subsetIndexHelper(t, result, expectedResult)

	if subsetIndexes(1, 2) != nil {
		t.Log("Subset indexes returned a subset where the length is greater than the len")
		t.Fail()
	}

}

func subsetIndexHelper(t *testing.T, result [][]int, expectedResult [][]int) {
	if len(result) != len(expectedResult) {
		t.Log("subset indexes returned wrong number of results for: ", result, " :", expectedResult)
		t.FailNow()
	}
	for i, item := range result {
		if len(item) != len(expectedResult[0]) {
			t.Log("subset indexes returned a result with wrong numbrer of items ", i, " : ", result, " : ", expectedResult)
			t.FailNow()
		}
		for j, value := range item {
			if value != expectedResult[i][j] {
				t.Log("Subset indexes had wrong number at ", i, ",", j, " : ", result, " : ", expectedResult)
				t.Fail()
			}
		}
	}
}

type solveTechniqueMatchMode int

const (
	solveTechniqueMatchModeAll = iota
	solveTechniqueMatchModeAny
)

type solveTechniqueTestHelperOptions struct {
	transpose bool
	//Whether the descriptions of cells are a list of legal possible individual values, or must all match.
	matchMode    solveTechniqueMatchMode
	targetCells  []cellRef
	pointerCells []cellRef
	targetNums   IntSlice
	pointerNums  IntSlice
	targetSame   cellGroupType
	targetGroup  int
	//If description provided, the description MUST match.
	description string
	//If descriptions provided, ONE of the descriptions must match.
	//generally used in conjunction with solveTechniqueMatchModeAny.
	descriptions []string
	debugPrint   bool
}

func humanSolveTechniqueTestHelper(t *testing.T, puzzleName string, techniqueName string, options solveTechniqueTestHelperOptions) {
	//TODO: test for col and block as well
	grid := NewGrid()
	grid.LoadFromFile(puzzlePath(puzzleName))

	if options.transpose {
		grid = grid.transpose()
	}

	solver := techniquesByName[techniqueName]

	if solver == nil {
		t.Fatal("Couldn't find technique object: ", techniqueName)
	}

	steps := solver.Find(grid)

	if len(steps) == 0 {
		t.Fatal(techniqueName, " didn't find a cell it should have.")
	}

	step := steps[0]

	if options.debugPrint {
		log.Println(step)
	}

	if options.matchMode == solveTechniqueMatchModeAll {

		//All must match

		if options.targetCells != nil {
			if !step.TargetCells.sameAsRefs(options.targetCells) {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			if !step.PointerCells.sameAsRefs(options.pointerCells) {
				t.Error(techniqueName, " had the wrong pointer cells: ", step.PointerCells)
			}
		}

		switch options.targetSame {
		case GROUP_ROW:
			if !step.TargetCells.SameRow() || step.TargetCells.Row() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong row :", step.TargetCells.Row())
			}
		case GROUP_BLOCK:
			if !step.TargetCells.SameBlock() || step.TargetCells.Block() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong block :", step.TargetCells.Block())
			}
		case GROUP_COL:
			if !step.TargetCells.SameCol() || step.TargetCells.Col() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong col :", step.TargetCells.Col())
			}
		case GROUP_NONE:
			//Do nothing
		default:
			t.Error("human solve technique helper error: unsupported group type: ", options.targetSame)
		}

		if options.targetNums != nil {
			if !step.TargetNums.SameContentAs(options.targetNums) {
				t.Error(techniqueName, " found the wrong numbers: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			if !step.PointerNums.SameContentAs(options.pointerNums) {
				t.Error(techniqueName, "found the wrong numbers:", step.PointerNums)
			}
		}
	} else if options.matchMode == solveTechniqueMatchModeAny {

		foundMatch := false

		if options.targetCells != nil {
			foundMatch = false
			for _, ref := range options.targetCells {
				for _, cell := range step.TargetCells {
					if ref.Cell(grid) == cell {
						//TODO: break out early
						foundMatch = true
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			t.Error("Pointer cells in match mode any not yet supported.")
		}

		if options.targetSame != GROUP_NONE {
			t.Error("Target Same in match mode any not yet supported.")
		}

		if options.targetNums != nil {
			foundMatch = false
			for _, targetNum := range options.targetNums {
				for _, num := range step.TargetNums {
					if targetNum == num {
						foundMatch = true
						//TODO: break early here.
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target nums: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			foundMatch = false
			for _, pointerNum := range options.pointerNums {
				for _, num := range step.PointerNums {
					if pointerNum == num {
						foundMatch = true
						//TODO: break early here
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong pointer nums: ", step.PointerNums)
			}
		}
	}

	if options.description != "" {
		//Normalize the step so that the description will be stable for the test.
		step.normalize()
		description := solver.Description(step)
		if description != options.description {
			t.Error("Wrong description for ", techniqueName, ". Got:*", description, "* expected: *", options.description, "*")
		}
	} else if options.descriptions != nil {
		foundMatch := false
		step.normalize()
		description := solver.Description(step)
		for _, targetDescription := range options.descriptions {
			if description == targetDescription {
				foundMatch = true
			}
		}
		if !foundMatch {
			t.Error("No descriptions matched for ", techniqueName, ". Got:*", description)
		}
	}

	//TODO: we should do exhaustive testing of SolveStep application. We used to test it here, but as long as targetCells and targetNums are correct it should be fine.

	grid.Done()
}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes 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 e2e

import (
	"fmt"
	"net/http"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var c *client.Client = nil

func LaunchNetTestPodPerNode(nodes *api.NodeList, name string, c *client.Client, ns string) []string {
	podNames := []string{}

	totalPods := len(nodes.Items)

	Expect(totalPods).NotTo(Equal(0))

	for i, node := range nodes.Items {
		podName := fmt.Sprintf("%s-%d", name, i)
		podNames = append(podNames, podName)
		Logf("Creating pod %s on node %s", podName, node.Name)
		_, err := c.Pods(ns).Create(&api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: podName,
				Labels: map[string]string{
					"name": name,
				},
			},
			Spec: api.PodSpec{
				Containers: []api.Container{
					{
						Name:  "webserver",
						Image: "gcr.io/google_containers/nettest:1.3",
						Args: []string{
							"-service=" + name,
							//peers >= totalPods should be asserted by the container.
							//the nettest container finds peers by looking up list of svc endpoints.
							fmt.Sprintf("-peers=%d", totalPods),
							"-namespace=" + ns},
						Ports: []api.ContainerPort{{ContainerPort: 8080}},
					},
				},
				Host:          node.Name,
				RestartPolicy: api.RestartPolicyNever,
			},
		})
		Expect(err).NotTo(HaveOccurred())
	}
	return podNames
}

var _ = Describe("Networking", func() {

	//This namespace is modified throughout the course of the test.
	var namespace *api.Namespace
	var svcname = "nettest"
	var c *client.Client = nil

	BeforeEach(func() {
		//Assert basic external connectivity.
		//Since this is not really a test of kubernetes in any way, we
		//leave it as a pre-test assertion, rather than a Ginko test.
		By("Executing a successful http request from the external internet")
		resp, err := http.Get("http://google.com")
		if err != nil {
			Failf("Unable to connect/talk to the internet: %v", err)
		}
		if resp.StatusCode != http.StatusOK {
			Failf("Unexpected error code, expected 200, got, %v (%v)", resp.StatusCode, resp)
		}

		By("Creating a kubernetes client")
		c, err = loadClient()
		Expect(err).NotTo(HaveOccurred())

		By("Building a namespace api object")
		namespace, err = createTestingNS("nettest", c)
		Expect(err).NotTo(HaveOccurred())

	})

	AfterEach(func() {
		By(fmt.Sprintf("Destroying namespace for this suite %v", namespace.Name))
		if err := c.Namespaces().Delete(namespace.Name); err != nil {
			Failf("Couldn't delete ns %s", err)
		}
	})

	// First test because it has no dependencies on variables created later on.
	It("should provide unchanging, static URL paths for kubernetes api services.", func() {
		tests := []struct {
			path string
		}{
			{path: "/validate"},
			{path: "/healthz"},
			// TODO: test proxy links here
		}
		for _, test := range tests {
			By(fmt.Sprintf("testing: %s", test.path))
			data, err := c.RESTClient.Get().
				Namespace(namespace.Name).
				AbsPath(test.path).
				DoRaw()
			if err != nil {
				Failf("Failed: %v\nBody: %s", err, string(data))
			}
		}
	})

	//Now we can proceed with the test.
	It("should function for intra-pod communication", func() {

		if testContext.Provider == "vagrant" {
			By("Skipping test which is broken for vagrant (See https://github.com/GoogleCloudPlatform/kubernetes/issues/3580)")
			return
		}

		By(fmt.Sprintf("Creating a service named [%s] in namespace %s", svcname, namespace.Name))
		svc, err := c.Services(namespace.Name).Create(&api.Service{
			ObjectMeta: api.ObjectMeta{
				Name: svcname,
				Labels: map[string]string{
					"name": svcname,
				},
			},
			Spec: api.ServiceSpec{
				Ports: []api.ServicePort{{
					Protocol:   "TCP",
					Port:       8080,
					TargetPort: util.NewIntOrStringFromInt(8080),
				}},
				Selector: map[string]string{
					"name": svcname,
				},
			},
		})
		if err != nil {
			Failf("unable to create test service named [%s] %v", svc.Name, err)
		}

		// Clean up service
		defer func() {
			defer GinkgoRecover()
			By("Cleaning up the service")
			if err = c.Services(namespace.Name).Delete(svc.Name); err != nil {
				Failf("unable to delete svc %v: %v", svc.Name, err)
			}
		}()

		By("Creating a webserver (pending) pod on each node")

		nodes, err := c.Nodes().List(labels.Everything(), fields.Everything())
		if err != nil {
			Failf("Failed to list nodes: %v", err)
		}

		podNames := LaunchNetTestPodPerNode(nodes, svcname, c, namespace.Name)

		// Clean up the pods
		defer func() {
			defer GinkgoRecover()
			By("Cleaning up the webserver pods")
			for _, podName := range podNames {
				if err = c.Pods(namespace.Name).Delete(podName, nil); err != nil {
					Logf("Failed to delete pod %s: %v", podName, err)
				}
			}
		}()

		By("Waiting for the webserver pods to transition to Running state")
		for _, podName := range podNames {
			err = waitForPodRunningInNamespace(c, podName, namespace.Name)
			Expect(err).NotTo(HaveOccurred())
		}

		By("Waiting for connectivity to be verified")
		const maxAttempts = 60
		passed := false

		//once response OK, evaluate response body for pass/fail.
		var body []byte

		for i := 0; i < maxAttempts && !passed; i++ {
			time.Sleep(2 * time.Second)
			Logf("About to make a proxy status call")
			start := time.Now()
			body, err = c.Get().
				Namespace(namespace.Name).
				Prefix("proxy").
				Resource("services").
				Name(svc.Name).
				Suffix("status").
				DoRaw()
			Logf("Proxy status call returned in %v", time.Since(start))
			if err != nil {
				Logf("Attempt %v/%v: service/pod still starting. (error: '%v')", i, maxAttempts, err)
				continue
			}
			//Finally, we pass/fail the test based on if the container's response body, as to wether or not it was able to find peers.
			switch string(body) {
			case "pass":
				Logf("Passed on attempt %v. Cleaning up.", i)
				passed = true
				break
			case "running":
				Logf("Attempt %v/%v: test still running", i, maxAttempts)
				break
			case "fail":
				if body, err = c.Get().
					Namespace(namespace.Name).Prefix("proxy").
					Resource("services").
					Name(svc.Name).Suffix("read").
					DoRaw(); err != nil {
					Failf("Failed on attempt %v. Cleaning up. Error reading details: %v", i, err)
				} else {
					Failf("Failed on attempt %v. Cleaning up. Details:\n%s", i, string(body))
				}
				break
			}
		}

		if !passed {
			if body, err = c.Get().
				Namespace(namespace.Name).
				Prefix("proxy").
				Resource("services").
				Name(svc.Name).
				Suffix("read").
				DoRaw(); err != nil {
				Failf("Timed out. Cleaning up. Error reading details: %v", err)
			} else {
				Failf("Timed out. Cleaning up. Details:\n%s", string(body))
			}
		}
		Expect(string(body)).To(Equal("pass"))
	})

})
<commit_msg>improve network test logging<commit_after>/*
Copyright 2014 The Kubernetes 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 e2e

import (
	"fmt"
	"net/http"
	"time"

	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var c *client.Client = nil

func LaunchNetTestPodPerNode(nodes *api.NodeList, name string, c *client.Client, ns string) []string {
	podNames := []string{}

	totalPods := len(nodes.Items)

	Expect(totalPods).NotTo(Equal(0))

	for i, node := range nodes.Items {
		podName := fmt.Sprintf("%s-%d", name, i)
		podNames = append(podNames, podName)
		Logf("Creating pod %s on node %s", podName, node.Name)
		_, err := c.Pods(ns).Create(&api.Pod{
			ObjectMeta: api.ObjectMeta{
				Name: podName,
				Labels: map[string]string{
					"name": name,
				},
			},
			Spec: api.PodSpec{
				Containers: []api.Container{
					{
						Name:  "webserver",
						Image: "gcr.io/google_containers/nettest:1.3",
						Args: []string{
							"-service=" + name,
							//peers >= totalPods should be asserted by the container.
							//the nettest container finds peers by looking up list of svc endpoints.
							fmt.Sprintf("-peers=%d", totalPods),
							"-namespace=" + ns},
						Ports: []api.ContainerPort{{ContainerPort: 8080}},
					},
				},
				Host:          node.Name,
				RestartPolicy: api.RestartPolicyNever,
			},
		})
		Expect(err).NotTo(HaveOccurred())
	}
	return podNames
}

var _ = Describe("Networking", func() {

	//This namespace is modified throughout the course of the test.
	var namespace *api.Namespace
	var svcname = "nettest"
	var c *client.Client = nil

	BeforeEach(func() {
		//Assert basic external connectivity.
		//Since this is not really a test of kubernetes in any way, we
		//leave it as a pre-test assertion, rather than a Ginko test.
		By("Executing a successful http request from the external internet")
		resp, err := http.Get("http://google.com")
		if err != nil {
			Failf("Unable to connect/talk to the internet: %v", err)
		}
		if resp.StatusCode != http.StatusOK {
			Failf("Unexpected error code, expected 200, got, %v (%v)", resp.StatusCode, resp)
		}

		By("Creating a kubernetes client")
		c, err = loadClient()
		Expect(err).NotTo(HaveOccurred())

		By("Building a namespace api object")
		namespace, err = createTestingNS("nettest", c)
		Expect(err).NotTo(HaveOccurred())

	})

	AfterEach(func() {
		By(fmt.Sprintf("Destroying namespace for this suite %v", namespace.Name))
		if err := c.Namespaces().Delete(namespace.Name); err != nil {
			Failf("Couldn't delete ns %s", err)
		}
	})

	// First test because it has no dependencies on variables created later on.
	It("should provide unchanging, static URL paths for kubernetes api services.", func() {
		tests := []struct {
			path string
		}{
			{path: "/validate"},
			{path: "/healthz"},
			// TODO: test proxy links here
		}
		for _, test := range tests {
			By(fmt.Sprintf("testing: %s", test.path))
			data, err := c.RESTClient.Get().
				Namespace(namespace.Name).
				AbsPath(test.path).
				DoRaw()
			if err != nil {
				Failf("Failed: %v\nBody: %s", err, string(data))
			}
		}
	})

	//Now we can proceed with the test.
	It("should function for intra-pod communication", func() {

		if testContext.Provider == "vagrant" {
			By("Skipping test which is broken for vagrant (See https://github.com/GoogleCloudPlatform/kubernetes/issues/3580)")
			return
		}

		By(fmt.Sprintf("Creating a service named [%s] in namespace %s", svcname, namespace.Name))
		svc, err := c.Services(namespace.Name).Create(&api.Service{
			ObjectMeta: api.ObjectMeta{
				Name: svcname,
				Labels: map[string]string{
					"name": svcname,
				},
			},
			Spec: api.ServiceSpec{
				Ports: []api.ServicePort{{
					Protocol:   "TCP",
					Port:       8080,
					TargetPort: util.NewIntOrStringFromInt(8080),
				}},
				Selector: map[string]string{
					"name": svcname,
				},
			},
		})
		if err != nil {
			Failf("unable to create test service named [%s] %v", svc.Name, err)
		}

		// Clean up service
		defer func() {
			defer GinkgoRecover()
			By("Cleaning up the service")
			if err = c.Services(namespace.Name).Delete(svc.Name); err != nil {
				Failf("unable to delete svc %v: %v", svc.Name, err)
			}
		}()

		By("Creating a webserver (pending) pod on each node")

		nodes, err := c.Nodes().List(labels.Everything(), fields.Everything())
		if err != nil {
			Failf("Failed to list nodes: %v", err)
		}

		podNames := LaunchNetTestPodPerNode(nodes, svcname, c, namespace.Name)

		// Clean up the pods
		defer func() {
			defer GinkgoRecover()
			By("Cleaning up the webserver pods")
			for _, podName := range podNames {
				if err = c.Pods(namespace.Name).Delete(podName, nil); err != nil {
					Logf("Failed to delete pod %s: %v", podName, err)
				}
			}
		}()

		By("Waiting for the webserver pods to transition to Running state")
		for _, podName := range podNames {
			err = waitForPodRunningInNamespace(c, podName, namespace.Name)
			Expect(err).NotTo(HaveOccurred())
		}

		By("Waiting for connectivity to be verified")
		const maxAttempts = 60
		stopBy := time.Now().Add(2 * time.Minute)
		passed := false

		//once response OK, evaluate response body for pass/fail.
		var body []byte

		for i := 0; i < maxAttempts && !passed; i++ {
			if time.Now().After(stopBy) {
				Logf("Timeout exceeded")
				break
			}
			time.Sleep(2 * time.Second)
			Logf("About to make a proxy status call")
			start := time.Now()
			body, err = c.Get().
				Namespace(namespace.Name).
				Prefix("proxy").
				Resource("services").
				Name(svc.Name).
				Suffix("status").
				DoRaw()
			Logf("Proxy status call returned in %v", time.Since(start))
			if err != nil {
				Logf("Attempt %v/%v: service/pod still starting. (error: '%v')", i, maxAttempts, err)
				continue
			}
			//Finally, we pass/fail the test based on if the container's response body, as to wether or not it was able to find peers.
			switch string(body) {
			case "pass":
				Logf("Passed on attempt %v. Cleaning up.", i)
				passed = true
			case "running":
				Logf("Attempt %v/%v: test still running", i, maxAttempts)
			case "fail":
				if body, err = c.Get().
					Namespace(namespace.Name).Prefix("proxy").
					Resource("services").
					Name(svc.Name).Suffix("read").
					DoRaw(); err != nil {
					Failf("Failed on attempt %v. Cleaning up. Error reading details: %v", i, err)
				} else {
					Failf("Failed on attempt %v. Cleaning up. Details:\n%s", i, string(body))
				}
			default:
				Logf("Unexpected response: %q", body)
			}
		}

		if !passed {
			if body, err = c.Get().
				Namespace(namespace.Name).
				Prefix("proxy").
				Resource("services").
				Name(svc.Name).
				Suffix("read").
				DoRaw(); err != nil {
				Failf("Timed out. Cleaning up. Error reading details: %v", err)
			} else {
				Failf("Timed out. Cleaning up. Details:\n%s", string(body))
			}
		}
		Expect(string(body)).To(Equal("pass"))
	})

})
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/Netflix-Skunkworks/go-jira/jira/cli"
	"github.com/coryb/optigo"
	"github.com/op/go-logging"
	"gopkg.in/coryb/yaml.v2"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"

func main() {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")

	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultQueryFields := "summary,created,priority,status,reporter,assignee"
	defaultSort := "priority asc, created"
	defaultMaxResults := 500

	usage := func(ok bool) {
		printer := fmt.Printf
		if !ok {
			printer = func(format string, args ...interface{}) (int, error) {
				return fmt.Fprintf(os.Stderr, format, args...)
			}
			defer func() {
				os.Exit(1)
			}()
		} else {
			defer func() {
				os.Exit(0)
			}()
		}
		output := fmt.Sprintf(`
Usage:
  jira (ls|list) <Query Options>
  jira view ISSUE
  jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
  jira create [--noedit] [-p PROJECT] <Create Options>
  jira DUPLICATE dups ISSUE
  jira BLOCKER blocks ISSUE
  jira watch ISSUE [-w WATCHER]
  jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
  jira ack ISSUE [--edit] <Edit Options>
  jira close ISSUE [--edit] <Edit Options>
  jira resolve ISSUE [--edit] <Edit Options>
  jira reopen ISSUE [--edit] <Edit Options>
  jira start ISSUE [--edit] <Edit Options>
  jira stop ISSUE [--edit] <Edit Options>
  jira comment ISSUE [--noedit] <Edit Options>
  jira take ISSUE
  jira (assign|give) ISSUE ASSIGNEE
  jira fields
  jira issuelinktypes
  jira transmeta ISSUE
  jira editmeta ISSUE
  jira issuetypes [-p PROJECT]
  jira createmeta [-p PROJECT] [-i ISSUETYPE]
  jira transitions ISSUE
  jira export-templates [-d DIR] [-t template]
  jira (b|browse) ISSUE
  jira login
  jira ISSUE

General Options:
  -b --browse         Open your browser to the Jira issue
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -t --template=FILE  Template file to use for output/editing
  -u --user=USER      Username to use for authenticaion (default: %s)
  -v --verbose        Increase output logging

Query Options:
  -a --assignee=USER        Username assigned the issue
  -c --component=COMPONENT  Component to Search for
  -f --queryfields=FIELDS   Fields that are used in "list" template: (default: %s)
  -i --issuetype=ISSUETYPE  The Issue Type
  -l --limit=VAL            Maximum number of results to return in query (default: %d)
  -p --project=PROJECT      Project to Search for
  -q --query=JQL            Jira Query Language expression for the search
  -r --reporter=USER        Reporter to search for
  -s --sort=ORDER           For list operations, sort issues (default: %s)
  -w --watcher=USER         Watcher to add to issue (default: %s)
                            or Watcher to search for

Edit Options:
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY=VAL     Set custom key/value pairs

Create Options:
  -i --issuetype=ISSUETYPE  Jira Issue Type (default: Bug)
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY=VAL     Set custom key/value pairs

Command Options:
  -d --directory=DIR        Directory to export templates to (default: %s)
`, user, defaultQueryFields, defaultMaxResults, defaultSort, user, fmt.Sprintf("%s/.jira.d/templates", home))
		printer(output)
	}

	jiraCommands := map[string]string{
		"list":             "list",
		"ls":               "list",
		"view":             "view",
		"edit":             "edit",
		"create":           "create",
		"dups":             "dups",
		"blocks":           "blocks",
		"watch":            "watch",
		"trans":            "transition",
		"transition":       "transition",
		"ack":              "acknowledge",
		"acknowledge":      "acknowledge",
		"close":            "close",
		"resolve":          "resolve",
		"reopen":           "reopen",
		"start":            "start",
		"stop":             "stop",
		"comment":          "comment",
		"take":             "take",
		"assign":           "assign",
		"give":             "assign",
		"fields":           "fields",
		"issuelinktypes":   "issuelinktypes",
		"transmeta":        "transmeta",
		"editmeta":         "editmeta",
		"issuetypes":       "issuetypes",
		"createmeta":       "createmeta",
		"transitions":      "transitions",
		"export-templates": "export-templates",
		"browse":           "browse",
		"login":            "login",
	}

	defaults := map[string]interface{}{
		"user":        user,
		"queryfields": defaultQueryFields,
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        defaultSort,
		"max_results": defaultMaxResults,
	}
	opts := make(map[string]interface{})

	overrides := make(map[string]string)

	setopt := func(name string, value interface{}) {
		opts[name] = value
	}

	op := optigo.NewDirectAssignParser(map[string]interface{}{
		"h|help": usage,
		"v|verbose+": func() {
			logging.SetLevel(logging.GetLevel("")+1, "")
		},
		"dryrun":                setopt,
		"b|browse":              setopt,
		"editor=s":              setopt,
		"u|user=s":              setopt,
		"endpoint=s":            setopt,
		"t|template=s":          setopt,
		"q|query=s":             setopt,
		"p|project=s":           setopt,
		"c|component=s":         setopt,
		"a|assignee=s":          setopt,
		"i|issuetype=s":         setopt,
		"w|watcher=s":           setopt,
		"r|reporter=s":          setopt,
		"f|queryfields=s":       setopt,
		"s|sort=s":              setopt,
		"l|limit|max_results=i": setopt,
		"o|override=s%":         &overrides,
		"noedit":                setopt,
		"edit":                  setopt,
		"m|comment=s":           setopt,
		"d|dir|directory=s":     setopt,
	})

	if err := op.ProcessAll(os.Args[1:]); err != nil {
		log.Error("%s", err)
		usage(false)
	}
	args := op.Args
	opts["overrides"] = overrides

	command := "view"
	if len(args) > 0 {
		if alias, ok := jiraCommands[args[0]]; ok {
			command = alias
			args = args[1:]
		} else if len(args) > 1 {
			// look at second arg for "dups" and "blocks" commands
			if alias, ok := jiraCommands[args[1]]; ok {
				command = alias
				args = append(args[:1], args[2:]...)
			}
		}
	}

	os.Setenv("JIRA_OPERATION", command)
	loadConfigs(opts)

	// apply defaults
	for k, v := range defaults {
		if _, ok := opts[k]; !ok {
			log.Debug("Setting %q to %#v from defaults", k, v)
			opts[k] = v
		}
	}

	log.Debug("opts: %v", opts)
	log.Debug("args: %v", args)

	if _, ok := opts["endpoint"]; !ok {
		log.Error("endpoint option required.  Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := cli.New(opts)

	log.Debug("opts: %s", opts)

	setEditing := func(dflt bool) {
		log.Debug("Default Editing: %t", dflt)
		if dflt {
			if val, ok := opts["noedit"].(bool); ok && val {
				log.Debug("Setting edit = false")
				opts["edit"] = false
			} else {
				log.Debug("Setting edit = true")
				opts["edit"] = true
			}
		} else {
			if _, ok := opts["edit"].(bool); !ok {
				log.Debug("Setting edit = %t", dflt)
				opts["edit"] = dflt
			}
		}
	}

	var err error
	switch command {
	case "login":
		err = c.CmdLogin()
	case "fields":
		err = c.CmdFields()
	case "list":
		err = c.CmdList()
	case "edit":
		setEditing(true)
		if len(args) > 0 {
			err = c.CmdEdit(args[0])
		} else {
			var data interface{}
			if data, err = c.FindIssues(); err == nil {
				issues := data.(map[string]interface{})["issues"].([]interface{})
				for _, issue := range issues {
					if err = c.CmdEdit(issue.(map[string]interface{})["key"].(string)); err != nil {
						switch err.(type) {
						case cli.NoChangesFound:
							log.Warning("No Changes found: %s", err)
							err = nil
							continue
						}
						break
					}
				}
			}
		}
	case "editmeta":
		err = c.CmdEditMeta(args[0])
	case "transmeta":
		err = c.CmdTransitionMeta(args[0])
	case "issuelinktypes":
		err = c.CmdIssueLinkTypes()
	case "issuetypes":
		err = c.CmdIssueTypes()
	case "createmeta":
		err = c.CmdCreateMeta()
	case "create":
		setEditing(true)
		err = c.CmdCreate()
	case "transitions":
		err = c.CmdTransitions(args[0])
	case "blocks":
		err = c.CmdBlocks(args[0], args[1])
	case "dups":
		if err = c.CmdDups(args[0], args[1]); err == nil {
			opts["resolution"] = "Duplicate"
			err = c.CmdTransition(args[0], "close")
		}
	case "watch":
		err = c.CmdWatch(args[0])
	case "transition":
		setEditing(true)
		err = c.CmdTransition(args[0], args[1])
	case "close":
		setEditing(false)
		err = c.CmdTransition(args[0], "close")
	case "acknowledge":
		setEditing(false)
		err = c.CmdTransition(args[0], "acknowledge")
	case "reopen":
		setEditing(false)
		err = c.CmdTransition(args[0], "reopen")
	case "resolve":
		setEditing(false)
		err = c.CmdTransition(args[0], "resolve")
	case "start":
		setEditing(false)
		err = c.CmdTransition(args[0], "start")
	case "stop":
		setEditing(false)
		err = c.CmdTransition(args[0], "stop")
	case "comment":
		setEditing(true)
		err = c.CmdComment(args[0])
	case "take":
		err = c.CmdAssign(args[0], opts["user"].(string))
	case "browse":
		opts["browse"] = true
		err = c.Browse(args[0])
	case "export-tempaltes":
		err = c.CmdExportTemplates()
	case "assign":
		err = c.CmdAssign(args[0], args[1])
	default:
		err = c.CmdView(args[0])
	}

	if err != nil {
		log.Error("%s", err)
		os.Exit(1)
	}
	os.Exit(0)
}

func parseYaml(file string, opts map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, &opts)
	}
}

func populateEnv(opts map[string]interface{}) {
	for k, v := range opts {
		envName := fmt.Sprintf("JIRA_%s", strings.ToUpper(k))
		var val string
		switch t := v.(type) {
		case string:
			val = t
		case int, int8, int16, int32, int64:
			val = fmt.Sprintf("%d", t)
		case float32, float64:
			val = fmt.Sprintf("%f", t)
		case bool:
			val = fmt.Sprintf("%t", t)
		default:
			val = fmt.Sprintf("%v", t)
		}
		os.Setenv(envName, val)
	}
}

func loadConfigs(opts map[string]interface{}) {
	populateEnv(opts)
	paths := cli.FindParentPaths(".jira.d/config.yml")
	// prepend
	paths = append([]string{"/etc/jira-cli.yml"}, paths...)

	// iterate paths in reverse
	for i := len(paths) - 1; i >= 0; i-- {
		file := paths[i]
		if stat, err := os.Stat(file); err == nil {
			tmp := make(map[string]interface{})
			// check to see if config file is exectuable
			if stat.Mode()&0111 == 0 {
				parseYaml(file, tmp)
			} else {
				log.Debug("Found Executable Config file: %s", file)
				// it is executable, so run it and try to parse the output
				cmd := exec.Command(file)
				stdout := bytes.NewBufferString("")
				cmd.Stdout = stdout
				cmd.Stderr = bytes.NewBufferString("")
				if err := cmd.Run(); err != nil {
					log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
					os.Exit(1)
				}
				yaml.Unmarshal(stdout.Bytes(), &tmp)
			}
			for k, v := range tmp {
				if _, ok := opts[k]; !ok {
					log.Debug("Setting %q to %#v from %s", k, v, file)
					opts[k] = v
				}
			}
			populateEnv(opts)
		}
	}
}
<commit_msg>allow for command aliasing in conjunction with executable config files<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/Netflix-Skunkworks/go-jira/jira/cli"
	"github.com/coryb/optigo"
	"github.com/op/go-logging"
	"gopkg.in/coryb/yaml.v2"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
)

var log = logging.MustGetLogger("jira")
var format = "%{color}%{time:2006-01-02T15:04:05.000Z07:00} %{level:-5s} [%{shortfile}]%{color:reset} %{message}"

func main() {
	logBackend := logging.NewLogBackend(os.Stderr, "", 0)
	logging.SetBackend(
		logging.NewBackendFormatter(
			logBackend,
			logging.MustStringFormatter(format),
		),
	)
	logging.SetLevel(logging.NOTICE, "")

	user := os.Getenv("USER")
	home := os.Getenv("HOME")
	defaultQueryFields := "summary,created,priority,status,reporter,assignee"
	defaultSort := "priority asc, created"
	defaultMaxResults := 500

	usage := func(ok bool) {
		printer := fmt.Printf
		if !ok {
			printer = func(format string, args ...interface{}) (int, error) {
				return fmt.Fprintf(os.Stderr, format, args...)
			}
			defer func() {
				os.Exit(1)
			}()
		} else {
			defer func() {
				os.Exit(0)
			}()
		}
		output := fmt.Sprintf(`
Usage:
  jira (ls|list) <Query Options>
  jira view ISSUE
  jira edit [--noedit] <Edit Options> [ISSUE | <Query Options>]
  jira create [--noedit] [-p PROJECT] <Create Options>
  jira DUPLICATE dups ISSUE
  jira BLOCKER blocks ISSUE
  jira watch ISSUE [-w WATCHER]
  jira (trans|transition) TRANSITION ISSUE [--noedit] <Edit Options>
  jira ack ISSUE [--edit] <Edit Options>
  jira close ISSUE [--edit] <Edit Options>
  jira resolve ISSUE [--edit] <Edit Options>
  jira reopen ISSUE [--edit] <Edit Options>
  jira start ISSUE [--edit] <Edit Options>
  jira stop ISSUE [--edit] <Edit Options>
  jira comment ISSUE [--noedit] <Edit Options>
  jira take ISSUE
  jira (assign|give) ISSUE ASSIGNEE
  jira fields
  jira issuelinktypes
  jira transmeta ISSUE
  jira editmeta ISSUE
  jira issuetypes [-p PROJECT]
  jira createmeta [-p PROJECT] [-i ISSUETYPE]
  jira transitions ISSUE
  jira export-templates [-d DIR] [-t template]
  jira (b|browse) ISSUE
  jira login
  jira ISSUE

General Options:
  -b --browse         Open your browser to the Jira issue
  -e --endpoint=URI   URI to use for jira
  -h --help           Show this usage
  -t --template=FILE  Template file to use for output/editing
  -u --user=USER      Username to use for authenticaion (default: %s)
  -v --verbose        Increase output logging

Query Options:
  -a --assignee=USER        Username assigned the issue
  -c --component=COMPONENT  Component to Search for
  -f --queryfields=FIELDS   Fields that are used in "list" template: (default: %s)
  -i --issuetype=ISSUETYPE  The Issue Type
  -l --limit=VAL            Maximum number of results to return in query (default: %d)
  -p --project=PROJECT      Project to Search for
  -q --query=JQL            Jira Query Language expression for the search
  -r --reporter=USER        Reporter to search for
  -s --sort=ORDER           For list operations, sort issues (default: %s)
  -w --watcher=USER         Watcher to add to issue (default: %s)
                            or Watcher to search for

Edit Options:
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY=VAL     Set custom key/value pairs

Create Options:
  -i --issuetype=ISSUETYPE  Jira Issue Type (default: Bug)
  -m --comment=COMMENT      Comment message for transition
  -o --override=KEY=VAL     Set custom key/value pairs

Command Options:
  -d --directory=DIR        Directory to export templates to (default: %s)
`, user, defaultQueryFields, defaultMaxResults, defaultSort, user, fmt.Sprintf("%s/.jira.d/templates", home))
		printer(output)
	}

	jiraCommands := map[string]string{
		"list":             "list",
		"ls":               "list",
		"view":             "view",
		"edit":             "edit",
		"create":           "create",
		"dups":             "dups",
		"blocks":           "blocks",
		"watch":            "watch",
		"trans":            "transition",
		"transition":       "transition",
		"ack":              "acknowledge",
		"acknowledge":      "acknowledge",
		"close":            "close",
		"resolve":          "resolve",
		"reopen":           "reopen",
		"start":            "start",
		"stop":             "stop",
		"comment":          "comment",
		"take":             "take",
		"assign":           "assign",
		"give":             "assign",
		"fields":           "fields",
		"issuelinktypes":   "issuelinktypes",
		"transmeta":        "transmeta",
		"editmeta":         "editmeta",
		"issuetypes":       "issuetypes",
		"createmeta":       "createmeta",
		"transitions":      "transitions",
		"export-templates": "export-templates",
		"browse":           "browse",
		"login":            "login",
	}

	defaults := map[string]interface{}{
		"user":        user,
		"queryfields": defaultQueryFields,
		"directory":   fmt.Sprintf("%s/.jira.d/templates", home),
		"sort":        defaultSort,
		"max_results": defaultMaxResults,
	}
	opts := make(map[string]interface{})

	overrides := make(map[string]string)

	setopt := func(name string, value interface{}) {
		opts[name] = value
	}

	op := optigo.NewDirectAssignParser(map[string]interface{}{
		"h|help": usage,
		"v|verbose+": func() {
			logging.SetLevel(logging.GetLevel("")+1, "")
		},
		"dryrun":                setopt,
		"b|browse":              setopt,
		"editor=s":              setopt,
		"u|user=s":              setopt,
		"endpoint=s":            setopt,
		"t|template=s":          setopt,
		"q|query=s":             setopt,
		"p|project=s":           setopt,
		"c|component=s":         setopt,
		"a|assignee=s":          setopt,
		"i|issuetype=s":         setopt,
		"w|watcher=s":           setopt,
		"r|reporter=s":          setopt,
		"f|queryfields=s":       setopt,
		"s|sort=s":              setopt,
		"l|limit|max_results=i": setopt,
		"o|override=s%":         &overrides,
		"noedit":                setopt,
		"edit":                  setopt,
		"m|comment=s":           setopt,
		"d|dir|directory=s":     setopt,
	})

	if err := op.ProcessAll(os.Args[1:]); err != nil {
		log.Error("%s", err)
		usage(false)
	}
	args := op.Args
	opts["overrides"] = overrides

	var command string
	if len(args) > 0 {
		if alias, ok := jiraCommands[args[0]]; ok {
			command = alias
			args = args[1:]
		} else if len(args) > 1 {
			// look at second arg for "dups" and "blocks" commands
			if alias, ok := jiraCommands[args[1]]; ok {
				command = alias
				args = append(args[:1], args[2:]...)
			}
		}
	}

	if command == "" {
		command = args[0]
		args = args[1:]
	}

	os.Setenv("JIRA_OPERATION", command)
	loadConfigs(opts)

	// check to see if it was set in the configs:
	if value, ok := opts["command"].(string); ok {
		command = value
	} else {
		args = append([]string{command}, args...)
		command = "view"
	}

	// apply defaults
	for k, v := range defaults {
		if _, ok := opts[k]; !ok {
			log.Debug("Setting %q to %#v from defaults", k, v)
			opts[k] = v
		}
	}

	log.Debug("opts: %v", opts)
	log.Debug("args: %v", args)

	if _, ok := opts["endpoint"]; !ok {
		log.Error("endpoint option required.  Either use --endpoint or set a enpoint option in your ~/.jira.d/config.yml file")
		os.Exit(1)
	}

	c := cli.New(opts)

	log.Debug("opts: %s", opts)

	setEditing := func(dflt bool) {
		log.Debug("Default Editing: %t", dflt)
		if dflt {
			if val, ok := opts["noedit"].(bool); ok && val {
				log.Debug("Setting edit = false")
				opts["edit"] = false
			} else {
				log.Debug("Setting edit = true")
				opts["edit"] = true
			}
		} else {
			if _, ok := opts["edit"].(bool); !ok {
				log.Debug("Setting edit = %t", dflt)
				opts["edit"] = dflt
			}
		}
	}

	var err error
	switch command {
	case "login":
		err = c.CmdLogin()
	case "fields":
		err = c.CmdFields()
	case "list":
		err = c.CmdList()
	case "edit":
		setEditing(true)
		if len(args) > 0 {
			err = c.CmdEdit(args[0])
		} else {
			var data interface{}
			if data, err = c.FindIssues(); err == nil {
				issues := data.(map[string]interface{})["issues"].([]interface{})
				for _, issue := range issues {
					if err = c.CmdEdit(issue.(map[string]interface{})["key"].(string)); err != nil {
						switch err.(type) {
						case cli.NoChangesFound:
							log.Warning("No Changes found: %s", err)
							err = nil
							continue
						}
						break
					}
				}
			}
		}
	case "editmeta":
		err = c.CmdEditMeta(args[0])
	case "transmeta":
		err = c.CmdTransitionMeta(args[0])
	case "issuelinktypes":
		err = c.CmdIssueLinkTypes()
	case "issuetypes":
		err = c.CmdIssueTypes()
	case "createmeta":
		err = c.CmdCreateMeta()
	case "create":
		setEditing(true)
		err = c.CmdCreate()
	case "transitions":
		err = c.CmdTransitions(args[0])
	case "blocks":
		err = c.CmdBlocks(args[0], args[1])
	case "dups":
		if err = c.CmdDups(args[0], args[1]); err == nil {
			opts["resolution"] = "Duplicate"
			err = c.CmdTransition(args[0], "close")
		}
	case "watch":
		err = c.CmdWatch(args[0])
	case "transition":
		setEditing(true)
		err = c.CmdTransition(args[0], args[1])
	case "close":
		setEditing(false)
		err = c.CmdTransition(args[0], "close")
	case "acknowledge":
		setEditing(false)
		err = c.CmdTransition(args[0], "acknowledge")
	case "reopen":
		setEditing(false)
		err = c.CmdTransition(args[0], "reopen")
	case "resolve":
		setEditing(false)
		err = c.CmdTransition(args[0], "resolve")
	case "start":
		setEditing(false)
		err = c.CmdTransition(args[0], "start")
	case "stop":
		setEditing(false)
		err = c.CmdTransition(args[0], "stop")
	case "comment":
		setEditing(true)
		err = c.CmdComment(args[0])
	case "take":
		err = c.CmdAssign(args[0], opts["user"].(string))
	case "browse":
		opts["browse"] = true
		err = c.Browse(args[0])
	case "export-tempaltes":
		err = c.CmdExportTemplates()
	case "assign":
		err = c.CmdAssign(args[0], args[1])
	case "view":
		err = c.CmdView(args[0])
	default:
		log.Error("Unknown command %s", command)
		os.Exit(1)
	}

	if err != nil {
		log.Error("%s", err)
		os.Exit(1)
	}
	os.Exit(0)
}

func parseYaml(file string, opts map[string]interface{}) {
	if fh, err := ioutil.ReadFile(file); err == nil {
		log.Debug("Found Config file: %s", file)
		yaml.Unmarshal(fh, &opts)
	}
}

func populateEnv(opts map[string]interface{}) {
	for k, v := range opts {
		envName := fmt.Sprintf("JIRA_%s", strings.ToUpper(k))
		var val string
		switch t := v.(type) {
		case string:
			val = t
		case int, int8, int16, int32, int64:
			val = fmt.Sprintf("%d", t)
		case float32, float64:
			val = fmt.Sprintf("%f", t)
		case bool:
			val = fmt.Sprintf("%t", t)
		default:
			val = fmt.Sprintf("%v", t)
		}
		os.Setenv(envName, val)
	}
}

func loadConfigs(opts map[string]interface{}) {
	populateEnv(opts)
	paths := cli.FindParentPaths(".jira.d/config.yml")
	// prepend
	paths = append([]string{"/etc/jira-cli.yml"}, paths...)

	// iterate paths in reverse
	for i := len(paths) - 1; i >= 0; i-- {
		file := paths[i]
		if stat, err := os.Stat(file); err == nil {
			tmp := make(map[string]interface{})
			// check to see if config file is exectuable
			if stat.Mode()&0111 == 0 {
				parseYaml(file, tmp)
			} else {
				log.Debug("Found Executable Config file: %s", file)
				// it is executable, so run it and try to parse the output
				cmd := exec.Command(file)
				stdout := bytes.NewBufferString("")
				cmd.Stdout = stdout
				cmd.Stderr = bytes.NewBufferString("")
				if err := cmd.Run(); err != nil {
					log.Error("%s is exectuable, but it failed to execute: %s\n%s", file, err, cmd.Stderr)
					os.Exit(1)
				}
				yaml.Unmarshal(stdout.Bytes(), &tmp)
			}
			for k, v := range tmp {
				if _, ok := opts[k]; !ok {
					log.Debug("Setting %q to %#v from %s", k, v, file)
					opts[k] = v
				}
			}
			populateEnv(opts)
		}
	}
}
<|endoftext|>


<commit_before>package jira

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

const (
	testJIRAInstanceURL = "https://issues.apache.org/jira/"
)

var (
	// testMux is the HTTP request multiplexer used with the test server.
	testMux *http.ServeMux

	// testClient is the JIRA client being tested.
	testClient *Client

	// testServer is a test HTTP server used to provide mock API responses.
	testServer *httptest.Server
)

type testValues map[string]string

// setup sets up a test HTTP server along with a jira.Client that is configured to talk to that test server.
// Tests should register handlers on mux which provide mock responses for the API method being tested.
func setup() {
	// Test server
	testMux = http.NewServeMux()
	testServer = httptest.NewServer(testMux)

	// jira client configured to use test server
	testClient, _ = NewClient(nil, testServer.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	testServer.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

func testRequestURL(t *testing.T, r *http.Request, want string) {
	if got := r.URL.String(); got != want {
		t.Errorf("Request URL: %v, want %v", got, want)
	}
}

func TestNewClient_WrongUrl(t *testing.T) {
	c, err := NewClient(nil, "://issues.apache.org/jira/")

	if err == nil {
		t.Error("Expected an error. Got none")
	}
	if c != nil {
		t.Errorf("Expected no client. Got %+v", c)
	}
}

func TestNewClient_WithHttpClient(t *testing.T) {
	httpClient := http.DefaultClient
	httpClient.Timeout = 10 * time.Minute
	c, err := NewClient(httpClient, testJIRAInstanceURL)

	if err != nil {
		t.Errorf("Got an error: %s", err)
	}
	if c == nil {
		t.Error("Expected a client. Got none")
	}
	if !reflect.DeepEqual(c.client, httpClient) {
		t.Errorf("HTTP clients are not equal. Injected %+v, got %+v", httpClient, c.client)
	}
}

func TestNewClient_WithServices(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)

	if err != nil {
		t.Errorf("Got an error: %s", err)
	}
	if c.Authentication == nil {
		t.Error("No AuthenticationService provided")
	}
	if c.Issue == nil {
		t.Error("No IssueService provided")
	}
}

func TestCheckResponse_GoodResults(t *testing.T) {
	codes := []int{
		http.StatusOK, http.StatusPartialContent, 299,
	}

	for _, c := range codes {
		r := &http.Response{
			StatusCode: c,
		}
		if err := CheckResponse(r); err != nil {
			t.Errorf("CheckResponse throws an error: %s", err)
		}
	}
}

func TestNewRequest(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	inURL, outURL := "rest/api/2/issue/", testJIRAInstanceURL+"rest/api/2/issue/"
	inBody, outBody := &Issue{Key: "MESOS"}, `{"key":"MESOS"}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// Test that relative URL was expanded
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
	}

	// Test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%v) Body is %v, want %v", inBody, got, want)
	}
}

func TestNewRequest_InvalidJSON(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	type T struct {
		A map[int]interface{}
	}
	_, err = c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %+v.", err)
	}
}

func testURLParseError(t *testing.T, err error) {
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

func TestNewRequest_BadURL(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	_, err = c.NewRequest("GET", ":", nil)
	testURLParseError(t, err)
}

// If a nil body is passed to gerrit.NewRequest, make sure that nil is also passed to http.NewRequest.
// In most cases, passing an io.Reader that returns no content is fine,
// since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
// However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
func TestNewRequest_EmptyBody(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest returned unexpected error: %v", err)
	}
	if req.Body != nil {
		t.Fatalf("constructed request contains a non-nil Body")
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	testClient.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do() function.
// A redirect loop is pretty unlikely to occur within the Gerrit API, but does allow us to exercise the right code path.
func TestDo_RedirectLoop(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %+v.", err)
	}
}
<commit_msg>Add test cases for DoNoClose()<commit_after>package jira

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"net/url"
	"reflect"
	"testing"
	"time"
)

const (
	testJIRAInstanceURL = "https://issues.apache.org/jira/"
)

var (
	// testMux is the HTTP request multiplexer used with the test server.
	testMux *http.ServeMux

	// testClient is the JIRA client being tested.
	testClient *Client

	// testServer is a test HTTP server used to provide mock API responses.
	testServer *httptest.Server
)

type testValues map[string]string

// setup sets up a test HTTP server along with a jira.Client that is configured to talk to that test server.
// Tests should register handlers on mux which provide mock responses for the API method being tested.
func setup() {
	// Test server
	testMux = http.NewServeMux()
	testServer = httptest.NewServer(testMux)

	// jira client configured to use test server
	testClient, _ = NewClient(nil, testServer.URL)
}

// teardown closes the test HTTP server.
func teardown() {
	testServer.Close()
}

func testMethod(t *testing.T, r *http.Request, want string) {
	if got := r.Method; got != want {
		t.Errorf("Request method: %v, want %v", got, want)
	}
}

func testRequestURL(t *testing.T, r *http.Request, want string) {
	if got := r.URL.String(); got != want {
		t.Errorf("Request URL: %v, want %v", got, want)
	}
}

func TestNewClient_WrongUrl(t *testing.T) {
	c, err := NewClient(nil, "://issues.apache.org/jira/")

	if err == nil {
		t.Error("Expected an error. Got none")
	}
	if c != nil {
		t.Errorf("Expected no client. Got %+v", c)
	}
}

func TestNewClient_WithHttpClient(t *testing.T) {
	httpClient := http.DefaultClient
	httpClient.Timeout = 10 * time.Minute
	c, err := NewClient(httpClient, testJIRAInstanceURL)

	if err != nil {
		t.Errorf("Got an error: %s", err)
	}
	if c == nil {
		t.Error("Expected a client. Got none")
	}
	if !reflect.DeepEqual(c.client, httpClient) {
		t.Errorf("HTTP clients are not equal. Injected %+v, got %+v", httpClient, c.client)
	}
}

func TestNewClient_WithServices(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)

	if err != nil {
		t.Errorf("Got an error: %s", err)
	}
	if c.Authentication == nil {
		t.Error("No AuthenticationService provided")
	}
	if c.Issue == nil {
		t.Error("No IssueService provided")
	}
}

func TestCheckResponse_GoodResults(t *testing.T) {
	codes := []int{
		http.StatusOK, http.StatusPartialContent, 299,
	}

	for _, c := range codes {
		r := &http.Response{
			StatusCode: c,
		}
		if err := CheckResponse(r); err != nil {
			t.Errorf("CheckResponse throws an error: %s", err)
		}
	}
}

func TestNewRequest(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	inURL, outURL := "rest/api/2/issue/", testJIRAInstanceURL+"rest/api/2/issue/"
	inBody, outBody := &Issue{Key: "MESOS"}, `{"key":"MESOS"}`+"\n"
	req, _ := c.NewRequest("GET", inURL, inBody)

	// Test that relative URL was expanded
	if got, want := req.URL.String(), outURL; got != want {
		t.Errorf("NewRequest(%q) URL is %v, want %v", inURL, got, want)
	}

	// Test that body was JSON encoded
	body, _ := ioutil.ReadAll(req.Body)
	if got, want := string(body), outBody; got != want {
		t.Errorf("NewRequest(%v) Body is %v, want %v", inBody, got, want)
	}
}

func TestNewRequest_InvalidJSON(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}

	type T struct {
		A map[int]interface{}
	}
	_, err = c.NewRequest("GET", "/", &T{})

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*json.UnsupportedTypeError); !ok {
		t.Errorf("Expected a JSON error; got %+v.", err)
	}
}

func testURLParseError(t *testing.T, err error) {
	if err == nil {
		t.Errorf("Expected error to be returned")
	}
	if err, ok := err.(*url.Error); !ok || err.Op != "parse" {
		t.Errorf("Expected URL parse error, got %+v", err)
	}
}

func TestNewRequest_BadURL(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	_, err = c.NewRequest("GET", ":", nil)
	testURLParseError(t, err)
}

// If a nil body is passed to gerrit.NewRequest, make sure that nil is also passed to http.NewRequest.
// In most cases, passing an io.Reader that returns no content is fine,
// since there is no difference between an HTTP request body that is an empty string versus one that is not set at all.
// However in certain cases, intermediate systems may treat these differently resulting in subtle errors.
func TestNewRequest_EmptyBody(t *testing.T) {
	c, err := NewClient(nil, testJIRAInstanceURL)
	if err != nil {
		t.Errorf("An error occured. Expected nil. Got %+v.", err)
	}
	req, err := c.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatalf("NewRequest returned unexpected error: %v", err)
	}
	if req.Body != nil {
		t.Fatalf("constructed request contains a non-nil Body")
	}
}

func TestDo(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	testClient.Do(req, body)

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDo_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

func TestDoNoClose(t *testing.T) {
	setup()
	defer teardown()

	type foo struct {
		A string
	}

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		if m := "GET"; m != r.Method {
			t.Errorf("Request method = %v, want %v", r.Method, m)
		}
		fmt.Fprint(w, `{"A":"a"}`)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	body := new(foo)
	resp, _ := testClient.DoNoClose(req, body)
	defer resp.Body.Close()

	want := &foo{"a"}
	if !reflect.DeepEqual(body, want) {
		t.Errorf("Response body = %v, want %v", body, want)
	}
}

func TestDoNoClose_HTTPError(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Error(w, "Bad Request", 400)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	resp, err := testClient.DoNoClose(req, nil)
	defer resp.Body.Close()

	if err == nil {
		t.Error("Expected HTTP 400 error.")
	}
}

// Test handling of an error caused by the internal http client's Do() function.
// A redirect loop is pretty unlikely to occur within the Gerrit API, but does allow us to exercise the right code path.
func TestDo_RedirectLoop(t *testing.T) {
	setup()
	defer teardown()

	testMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.Redirect(w, r, "/", http.StatusFound)
	})

	req, _ := testClient.NewRequest("GET", "/", nil)
	_, err := testClient.Do(req, nil)

	if err == nil {
		t.Error("Expected error to be returned.")
	}
	if err, ok := err.(*url.Error); !ok {
		t.Errorf("Expected a URL error; got %+v.", err)
	}
}
<|endoftext|>