<commit_before>// Copyright 2017 zebra project
//
// 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 rib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"

	"github.com/coreswitch/netutil"
	"github.com/vishvananda/netlink/nl"
)

var RibShowHeader = `Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
       O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
`

var RibShowHeaderDB = `       > - selected route, * - FIB route
`

type RibShowParam struct {
	afi          int
	database     bool
	longerPrefix bool
	address      net.IP
	prefix       *netutil.Prefix
	anchor       *netutil.Prefix
	separator    bool
	typ          uint8
}

func ribTypeShortString(rib *Rib) string {
	switch rib.Type {
	case RIB_KERNEL:
		return "K "
	case RIB_CONNECTED:
		return "C "
	case RIB_STATIC:
		return "S "
	case RIB_RIP, RIB_RIPNG:
		return "R "
	case RIB_OSPF, RIB_OSPF6:
		return "O "
	case RIB_BGP:
		return "B "
	case RIB_ISIS:
		return "i "
	}
	return ""
}

func (v *Vrf) RibShowIPv4Nexthop(rib *Rib, n *Nexthop, offset int, out io.Writer) {
	if offset != 0 {
		fmt.Fprintf(out, "%*s", offset, " ")
	}

	if rib.Type != RIB_KERNEL && rib.Type != RIB_CONNECTED {
		fmt.Fprintf(out, " [%d/%d]", rib.Distance, rib.Metric)
	}

	if n.IsIfOnly() {
		fmt.Fprintf(out, " is directly connected %s", v.IfName(n.Index))
	} else {
		if n.IsAddrOnly() {
			fmt.Fprintf(out, " via %v", n.IP)
		} else {
			fmt.Fprintf(out, " via %v, %s", n.IP, v.IfName(n.Index))
		}
	}
	switch n.EncapType {
	case nl.LWTUNNEL_ENCAP_SEG6:
		fmt.Fprintf(out, " encap seg6 %s\n", n.EncapSeg6.String())
	default:
		fmt.Fprintf(out, "\n")
	}
}

func (v *Vrf) RibShowEntryJson(t *ShowTask, p *RibShowParam, rib *Rib) {
	if !p.database && !rib.IsSelectedFib() {
		return
	}
	if p.separator {
		t.Str += ","
	} else {
		p.separator = true
	}
	bytes, err := json.Marshal(rib)
	if err != nil {
		t.Str += "{}"
		return
	}
	t.Str += string(bytes)
}

func (v *Vrf) RibShowIPv4Entry(t *ShowTask, rib *Rib, database bool) {
	buf := new(bytes.Buffer)

	if !database && !rib.IsSelectedFib() {
		return
	}
	selected := ' '
	fib := ' '
	if database {
		if rib.IsFib() {
			fib = '*'
		}
		if rib.IsSelected() {
			selected = '>'
		}
	}
	fmt.Fprintf(buf, "%s %s %c%c %v", ribTypeShortString(rib), "  ", fib, selected, rib.Prefix)
	offset := len(rib.Prefix.String()) + 9

	if rib.Nexthop != nil {
		v.RibShowIPv4Nexthop(rib, rib.Nexthop, 0, buf)
	} else {
		for pos, nexthop := range rib.Nexthops {
			if pos == 0 {
				v.RibShowIPv4Nexthop(rib, nexthop, 0, buf)
			} else {
				v.RibShowIPv4Nexthop(rib, nexthop, offset, buf)
			}
		}
	}
	t.Str += buf.String()
}

func (v *Vrf) RibShowIPv6Entry(t *ShowTask, rib *Rib, database bool) {
	buf := new(bytes.Buffer)

	if !database && !rib.IsSelectedFib() {
		return
	}
	selected := ' '
	fib := ' '
	if database {
		if rib.IsFib() {
			fib = '*'
		}
		if rib.IsSelected() {
			selected = '>'
		}
	}
	fmt.Fprintf(buf, "%s %c%c %v [%d/%d]\n", ribTypeShortString(rib), fib, selected, rib.Prefix, rib.Distance, rib.Metric)

	if rib.Nexthop != nil {
		switch rib.Nexthop.EncapType {
		case nl.LWTUNNEL_ENCAP_SEG6:
			fmt.Fprintf(buf, "      encap seg6 %s\n", rib.Nexthop.EncapSeg6.String())
		}
		if rib.Nexthop.IsIfOnly() {
			fmt.Fprintf(buf, "      via %s, directly connected\n", v.IfName(rib.Nexthop.Index))
		} else {
			if rib.Nexthop.IsAddrOnly() {
				fmt.Fprintf(buf, "      via %v\n", rib.Nexthop.IP)
			} else {
				fmt.Fprintf(buf, "      via %v, %s\n", rib.Nexthop.IP, v.IfName(rib.Nexthop.Index))
			}
		}
	} else {
	}
	t.Str += buf.String()
}

func (v *Vrf) RibShow(t *ShowTask) {
	p := t.Index.(*RibShowParam)
	if t.First {
		if t.Json {
			t.Str = "["
		} else {
			t.Str = RibShowHeader
			if p.database {
				t.Str += RibShowHeaderDB
			}
			t.Str += "\n"
		}
	}

	ptree := v.ribTable[p.afi]

	var top *netutil.PtreeNode
	if p.anchor != nil {
		n := ptree.Acquire(p.anchor.IP, p.anchor.Length)
		top = ptree.Next(n)
	} else {
		top = ptree.Top()
	}

	var n *netutil.PtreeNode
	for n = top; n != nil; n = ptree.Next(n) {
		ribs := n.Item.(RibSlice)
		for _, rib := range ribs {
			if p.typ != 0 && rib.Type != p.typ {
				continue
			}
			if t.Json {
				v.RibShowEntryJson(t, p, rib)
			} else {
				if p.afi == AFI_IP {
					v.RibShowIPv4Entry(t, rib, p.database)
				} else {
					v.RibShowIPv6Entry(t, rib, p.database)
				}
			}
		}
		p.anchor = netutil.PrefixFromNode(n)
		t.Continue = true
		ptree.Release(n)
		break
	}

	if n == nil && t.Json {
		t.Str += "]"
	}
}

func RibShow(vrfName string, t *ShowTask) {
	vrf := VrfLookupByName(vrfName)
	if vrf == nil {
		return
	}
	vrf.RibShow(t)
}

func ShowIpRoute(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpRouteType(t *ShowTask, Args []interface{}) {
	typ := RibStringType(Args[0].(string))
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
			typ: typ,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpRouteDatabase(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP,
			database: true,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpRouteVrf(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpRouteVrfType(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	typ := RibStringType(Args[1].(string))
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
			typ: typ,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpRouteVrfDatabase(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP,
			database: true,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpv6Route(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP6,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpv6RouteDatabase(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP6,
			database: true,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpv6RouteVrf(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP6,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpv6RouteVrfDatabase(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP6,
			database: true,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}
<commit_msg>Handle FIB only route show.<commit_after>// Copyright 2017 zebra project
//
// 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 rib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net"

	"github.com/coreswitch/netutil"
	"github.com/hash-set/zebra/policy"
	"github.com/vishvananda/netlink/nl"
)

var RibShowHeader = `Codes: K - kernel, C - connected, S - static, R - RIP, B - BGP
       O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, L1 - IS-IS level-1, L2 - IS-IS level-2, ia - IS-IS inter area
`

var RibShowHeaderDB = `       > - selected route, * - FIB route
`

type RibShowParam struct {
	afi          int
	database     bool
	longerPrefix bool
	address      net.IP
	prefix       *netutil.Prefix
	anchor       *netutil.Prefix
	separator    bool
	typ          uint8
}

func ribTypeShortString(rib *Rib) string {
	switch rib.Type {
	case RIB_KERNEL:
		return "K "
	case RIB_CONNECTED:
		return "C "
	case RIB_STATIC:
		return "S "
	case RIB_RIP:
		return "R "
	case RIB_OSPF:
		return "O "
	case RIB_BGP:
		return "B "
	case RIB_ISIS:
		return "i "
	}
	return ""
}

func (v *Vrf) RibShowIPv4Nexthop(rib *Rib, n *Nexthop, offset int, out io.Writer) {
	if offset != 0 {
		fmt.Fprintf(out, "%*s", offset, " ")
	}

	if rib.Type != RIB_KERNEL && rib.Type != RIB_CONNECTED {
		fmt.Fprintf(out, " [%d/%d]", rib.Distance, rib.Metric)
	}

	if n.IsIfOnly() {
		fmt.Fprintf(out, " is directly connected %s", v.IfName(n.Index))
	} else {
		if n.IsAddrOnly() {
			fmt.Fprintf(out, " via %v", n.IP)
		} else {
			fmt.Fprintf(out, " via %v, %s", n.IP, v.IfName(n.Index))
		}
	}
	switch n.EncapType {
	case nl.LWTUNNEL_ENCAP_SEG6:
		fmt.Fprintf(out, " encap seg6 %s", n.EncapSeg6.String())
	}
}

func (v *Vrf) RibShowEntryJson(t *ShowTask, p *RibShowParam, rib *Rib) {
	if !p.database && !rib.IsFib() {
		return
	}
	if p.separator {
		t.Str += ","
	} else {
		p.separator = true
	}
	bytes, err := json.Marshal(rib)
	if err != nil {
		t.Str += "{}"
		return
	}
	t.Str += string(bytes)
}

func (v *Vrf) RibShowIPv4Entry(t *ShowTask, rib *Rib, database bool) {
	buf := new(bytes.Buffer)

	if !database && !rib.IsFib() {
		return
	}
	selected := ' '
	fib := ' '
	if database {
		if rib.IsFib() {
			fib = '*'
		}
		if rib.IsSelected() {
			selected = '>'
		}
	}
	fmt.Fprintf(buf, "%s %s %c%c %v", ribTypeShortString(rib), "  ", fib, selected, rib.Prefix)
	offset := len(rib.Prefix.String()) + 9

	for pos, nexthop := range rib.Nexthops {
		if pos == 0 {
			v.RibShowIPv4Nexthop(rib, nexthop, 0, buf)
		} else {
			v.RibShowIPv4Nexthop(rib, nexthop, offset, buf)
		}
	}
	if rib.Aux != nil {
		aspath := &policy.ASPath{}
		aspath.DecodeFromBytes(rib.Aux)
		fmt.Fprintf(buf, " aspath %s", aspath.String())
	}
	if rib.PathId != 0 {
		fmt.Fprintf(buf, " pathid %d", rib.PathId)
	}
	fmt.Fprintf(buf, "\n")
	t.Str += buf.String()
}

func (v *Vrf) RibShowIPv6Entry(t *ShowTask, rib *Rib, database bool) {
	buf := new(bytes.Buffer)

	if !database && !rib.IsFib() {
		return
	}
	selected := ' '
	fib := ' '
	if database {
		if rib.IsFib() {
			fib = '*'
		}
		if rib.IsSelected() {
			selected = '>'
		}
	}
	fmt.Fprintf(buf, "%s %c%c %v [%d/%d]\n", ribTypeShortString(rib), fib, selected, rib.Prefix, rib.Distance, rib.Metric)

	if len(rib.Nexthops) == 1 {
		nhop := rib.Nexthops[0]
		switch nhop.EncapType {
		case nl.LWTUNNEL_ENCAP_SEG6:
			fmt.Fprintf(buf, "      encap seg6 %s\n", nhop.EncapSeg6.String())
		}
		if nhop.IsIfOnly() {
			fmt.Fprintf(buf, "      via %s, directly connected\n", v.IfName(nhop.Index))
		} else {
			if nhop.IsAddrOnly() {
				fmt.Fprintf(buf, "      via %v\n", nhop.IP)
			} else {
				fmt.Fprintf(buf, "      via %v, %s\n", nhop.IP, v.IfName(nhop.Index))
			}
		}
	}
	t.Str += buf.String()
}

func (v *Vrf) RibShow(t *ShowTask) {
	p := t.Index.(*RibShowParam)
	if t.First {
		if t.Json {
			t.Str = "["
		} else {
			t.Str = RibShowHeader
			if p.database {
				t.Str += RibShowHeaderDB
			}
			t.Str += "\n"
		}
	}

	ptree := v.ribTable[p.afi]

	var top *netutil.PtreeNode
	if p.anchor != nil {
		n := ptree.Acquire(p.anchor.IP, p.anchor.Length)
		top = ptree.Next(n)
	} else {
		top = ptree.Top()
	}

	var n *netutil.PtreeNode
	for n = top; n != nil; n = ptree.Next(n) {
		ribs := n.Item.(RibSlice)
		for _, rib := range ribs {
			if p.typ != 0 && rib.Type != p.typ {
				continue
			}
			if t.Json {
				v.RibShowEntryJson(t, p, rib)
			} else {
				if p.afi == AFI_IP {
					v.RibShowIPv4Entry(t, rib, p.database)
				} else {
					v.RibShowIPv6Entry(t, rib, p.database)
				}
			}
		}
		p.anchor = netutil.PrefixFromNode(n)
		t.Continue = true
		ptree.Release(n)
		break
	}

	if n == nil && t.Json {
		t.Str += "]"
	}
}

func RibShow(vrfName string, t *ShowTask) {
	vrf := VrfLookupByName(vrfName)
	if vrf == nil {
		return
	}
	vrf.RibShow(t)
}

func ShowIpRoute(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpRouteType(t *ShowTask, Args []interface{}) {
	typ := RibStringType(Args[0].(string))
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
			typ: typ,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpRouteDatabase(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP,
			database: true,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpRouteVrf(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpRouteVrfType(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	typ := RibStringType(Args[1].(string))
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP,
			typ: typ,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpRouteVrfDatabase(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP,
			database: true,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpv6Route(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP6,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpv6RouteDatabase(t *ShowTask, Args []interface{}) {
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP6,
			database: true,
		}
		t.Index = param
	}
	RibShow("", t)
}

func ShowIpv6RouteVrf(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi: AFI_IP6,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}

func ShowIpv6RouteVrfDatabase(t *ShowTask, Args []interface{}) {
	vrfName := Args[0].(string)
	if t.First {
		param := &RibShowParam{
			afi:      AFI_IP6,
			database: true,
		}
		t.Index = param
	}
	RibShow(vrfName, t)
}
<|endoftext|>


<commit_before>package main

import "testing"

type EvalData struct {
	in  complex128
	out complex128
}

type DegreeData struct {
	in  poly
	out int
}

type NormData struct {
	in  []complex128
	out []complex128
}

type ArithData struct {
	in1 []complex128
	in2 []complex128
	out []complex128
}

func TestPolyEvaluate(t *testing.T) {
	f := NewPoly(1, 2, 1)
	tests := []EvalData{
		{0, 1},
		{1, 4},
		{1i, 2i},
		{1 + 1i, 3 + 4i},
		{2 - 3i, -18i},
	}
	for _, test := range tests {
		val := f.Evaluate(test.in)
		t.Logf("testing at %v, got %v", test.in, val)
		if val != test.out {
			t.Errorf("did not evaluate to expected %v at %v", test.out, test.in)
		}
	}
}

func TestPolyDegree(t *testing.T) {
	tests := []DegreeData{
		{poly([]complex128{0}), 0},
		{poly([]complex128{1}), 0},
		{poly([]complex128{0, 1}), 1},
		{poly([]complex128{1, 1, 0}), 1},
	}
	for _, test := range tests {
		deg := test.in.Degree()
		t.Logf("deg %v is %v", test.in, deg)
		if deg != test.out {
			t.Errorf("did not get expected degree %v for %v", test.out, test.in)
		}
	}
}

func TestPolyNormalise(t *testing.T) {
	tests := []NormData{
		{[]complex128{0}, []complex128{0}},
		{[]complex128{0, 0}, []complex128{0}},
		{[]complex128{1}, []complex128{1}},
		{[]complex128{1, 0, 1}, []complex128{1, 0, 1}},
		{[]complex128{1, 0, 1, 0}, []complex128{1, 0, 1}},
		{[]complex128{1, 1, 0}, []complex128{1, 1}},
	}
	for _, test := range tests {
		in := poly(test.in)
		out := poly(test.out)
		norm := in.Normalise()
		t.Logf("(%v).Normalise() is %v", in, norm)
		if !norm.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}

func TestPolyEqual(t *testing.T) {
	var f, g poly

	f = NewPoly()
	g = NewPoly(0, 0, 0)
	if !f.Equal(g) {
		t.Errorf("%v != %v", f, g)
	}

	g = NewPoly(1)
	if f.Equal(g) {
		t.Errorf("%v == %v", f, g)
	}

	f = NewPoly(4, 4, 1)
	g = NewPoly(4, 4, 1)
	if !f.Equal(g) {
		t.Errorf("%v != %v", f, g)
	}
}

func TestPolyAdd(t *testing.T) {
	tests := []ArithData{
		{[]complex128{1}, []complex128{0}, []complex128{1}},
		{[]complex128{1}, []complex128{1, 1}, []complex128{2, 1}},
		{[]complex128{4, 4, 1}, []complex128{1, 2, 3}, []complex128{5, 6, 4}},
		{[]complex128{2, 1}, []complex128{-5}, []complex128{-3, 1}},
		{[]complex128{2, 1, 4, 1}, []complex128{1 + 1i}, []complex128{3 + 1i, 1, 4, 1}},
		{[]complex128{2, 1}, []complex128{-1, 3, -3, 1}, []complex128{1, 4, -3, 1}},
	}
	for _, test := range tests {
		in1 := poly(test.in1)
		in2 := poly(test.in2)
		out := poly(test.out)
		sum := in1.Add(in2)
		t.Logf("(%v) + (%v) = %v", in1, in2, sum)
		if !sum.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}

func TestPolySubtract(t *testing.T) {
	tests := []ArithData{
		{[]complex128{}, []complex128{}, []complex128{}},
		{[]complex128{1}, []complex128{}, []complex128{1}},
		{[]complex128{1}, []complex128{1, 1}, []complex128{0, -1}},
		{[]complex128{4, 4, 1}, []complex128{1, 2, 3}, []complex128{3, 2, -2}},
		{[]complex128{2, 1}, []complex128{-5}, []complex128{7, 1}},
		{[]complex128{2, 1, 4, 1}, []complex128{1 + 1i}, []complex128{1 - 1i, 1, 4, 1}},
		{[]complex128{2, 1}, []complex128{-1, 3, -3, 1}, []complex128{3, -2, 3, -1}},
	}
	for _, test := range tests {
		in1 := poly(test.in1)
		in2 := poly(test.in2)
		out := poly(test.out)
		sum := in1.Subtract(in2)
		t.Logf("(%v) + (%v) = %v", in1, in2, sum)
		if !sum.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}

func TestPolyMultiply(t *testing.T) {
	tests := []ArithData{
		{[]complex128{}, []complex128{}, []complex128{}},
		{[]complex128{1}, []complex128{}, []complex128{}},
		{[]complex128{1}, []complex128{1, 1}, []complex128{1, 1}},
		{[]complex128{4, 4, 1}, []complex128{1, 2, 1}, []complex128{4, 12, 13, 6, 1}},
		{[]complex128{1, -2, 1}, []complex128{1, 1, 1, 1}, []complex128{1, -1, 0, 0, -1, 1}},
	}
	for _, test := range tests {
		in1 := poly(test.in1)
		in2 := poly(test.in2)
		out := poly(test.out)
		prod := in1.Multiply(in2)
		t.Logf("(%v) * (%v) = %v", in1, in2, prod)
		if !prod.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}
<commit_msg>Add description of tested poly to TestPolyEvaluate<commit_after>package main

import "testing"

type EvalData struct {
	in  complex128
	out complex128
}

type DegreeData struct {
	in  poly
	out int
}

type NormData struct {
	in  []complex128
	out []complex128
}

type ArithData struct {
	in1 []complex128
	in2 []complex128
	out []complex128
}

func TestPolyEvaluate(t *testing.T) {
	f := NewPoly(1, 2, 1)
	tests := []EvalData{
		{0, 1},
		{1, 4},
		{1i, 2i},
		{1 + 1i, 3 + 4i},
		{2 - 3i, -18i},
	}
	for _, test := range tests {
		val := f.Evaluate(test.in)
		t.Logf("testing %v at %v, got %v", f, test.in, val)
		if val != test.out {
			t.Errorf("did not evaluate to expected %v at %v", test.out, test.in)
		}
	}
}

func TestPolyDegree(t *testing.T) {
	tests := []DegreeData{
		{poly([]complex128{0}), 0},
		{poly([]complex128{1}), 0},
		{poly([]complex128{0, 1}), 1},
		{poly([]complex128{1, 1, 0}), 1},
	}
	for _, test := range tests {
		deg := test.in.Degree()
		t.Logf("deg %v is %v", test.in, deg)
		if deg != test.out {
			t.Errorf("did not get expected degree %v for %v", test.out, test.in)
		}
	}
}

func TestPolyNormalise(t *testing.T) {
	tests := []NormData{
		{[]complex128{0}, []complex128{0}},
		{[]complex128{0, 0}, []complex128{0}},
		{[]complex128{1}, []complex128{1}},
		{[]complex128{1, 0, 1}, []complex128{1, 0, 1}},
		{[]complex128{1, 0, 1, 0}, []complex128{1, 0, 1}},
		{[]complex128{1, 1, 0}, []complex128{1, 1}},
	}
	for _, test := range tests {
		in := poly(test.in)
		out := poly(test.out)
		norm := in.Normalise()
		t.Logf("(%v).Normalise() is %v", in, norm)
		if !norm.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}

func TestPolyEqual(t *testing.T) {
	var f, g poly

	f = NewPoly()
	g = NewPoly(0, 0, 0)
	if !f.Equal(g) {
		t.Errorf("%v != %v", f, g)
	}

	g = NewPoly(1)
	if f.Equal(g) {
		t.Errorf("%v == %v", f, g)
	}

	f = NewPoly(4, 4, 1)
	g = NewPoly(4, 4, 1)
	if !f.Equal(g) {
		t.Errorf("%v != %v", f, g)
	}
}

func TestPolyAdd(t *testing.T) {
	tests := []ArithData{
		{[]complex128{1}, []complex128{0}, []complex128{1}},
		{[]complex128{1}, []complex128{1, 1}, []complex128{2, 1}},
		{[]complex128{4, 4, 1}, []complex128{1, 2, 3}, []complex128{5, 6, 4}},
		{[]complex128{2, 1}, []complex128{-5}, []complex128{-3, 1}},
		{[]complex128{2, 1, 4, 1}, []complex128{1 + 1i}, []complex128{3 + 1i, 1, 4, 1}},
		{[]complex128{2, 1}, []complex128{-1, 3, -3, 1}, []complex128{1, 4, -3, 1}},
	}
	for _, test := range tests {
		in1 := poly(test.in1)
		in2 := poly(test.in2)
		out := poly(test.out)
		sum := in1.Add(in2)
		t.Logf("(%v) + (%v) = %v", in1, in2, sum)
		if !sum.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}

func TestPolySubtract(t *testing.T) {
	tests := []ArithData{
		{[]complex128{}, []complex128{}, []complex128{}},
		{[]complex128{1}, []complex128{}, []complex128{1}},
		{[]complex128{1}, []complex128{1, 1}, []complex128{0, -1}},
		{[]complex128{4, 4, 1}, []complex128{1, 2, 3}, []complex128{3, 2, -2}},
		{[]complex128{2, 1}, []complex128{-5}, []complex128{7, 1}},
		{[]complex128{2, 1, 4, 1}, []complex128{1 + 1i}, []complex128{1 - 1i, 1, 4, 1}},
		{[]complex128{2, 1}, []complex128{-1, 3, -3, 1}, []complex128{3, -2, 3, -1}},
	}
	for _, test := range tests {
		in1 := poly(test.in1)
		in2 := poly(test.in2)
		out := poly(test.out)
		sum := in1.Subtract(in2)
		t.Logf("(%v) + (%v) = %v", in1, in2, sum)
		if !sum.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}

func TestPolyMultiply(t *testing.T) {
	tests := []ArithData{
		{[]complex128{}, []complex128{}, []complex128{}},
		{[]complex128{1}, []complex128{}, []complex128{}},
		{[]complex128{1}, []complex128{1, 1}, []complex128{1, 1}},
		{[]complex128{4, 4, 1}, []complex128{1, 2, 1}, []complex128{4, 12, 13, 6, 1}},
		{[]complex128{1, -2, 1}, []complex128{1, 1, 1, 1}, []complex128{1, -1, 0, 0, -1, 1}},
	}
	for _, test := range tests {
		in1 := poly(test.in1)
		in2 := poly(test.in2)
		out := poly(test.out)
		prod := in1.Multiply(in2)
		t.Logf("(%v) * (%v) = %v", in1, in2, prod)
		if !prod.Equal(out) {
			t.Error("did not get expected", out)
		}
	}
}
<|endoftext|>


<commit_before>package db

const BLANK = ""

const (
	DELETE_OPERATION = 1
	INSERT_OPERATION = 2
	UPDATE_OPERATION = 3

	SentCollection       = "sent_notifications"
	StatsCollection      = "last_seen_at"
	TopicCollection      = "topics"
	GullyCollection      = "gullys"
	UserLocaleCollection = "user_locales"

	SavedEmailCollection     = "saved_email"
	SavedPushCollection      = "saved_push"
	SavedWebCollection       = "saved_web"
	AvailableTopicCollection = "topics_available"

	DefaultsCollection  = "defaults"
	LocksCollection     = "locks"
	ProcessedCollection = "processed"
)

type Processed struct {
	BaseModel    `bson:",inline"`
	User         string `bson:"user"`
	Organization string `bson:"org"`
}

type Defaults struct {
	BaseModel    `bson:",inline"`
	Organization string   `json:"org" bson:"org"`
	Topic        string   `json:"ident" bson:"ident" required:"true"`
	Channels     []string `json:"channels" bson:"channels" required:"true"`
	Enabled      bool     `json:"enabled" bson:"enabled"`
}

type Locks struct {
	BaseModel    `bson:",inline"`
	Organization string   `json:"org" bson:"org" required:"true"`
	Topic        string   `json:"ident" bson:"ident" required:"true"`
	Channels     []string `json:"channels" bson:"channels" required:"true"`
	Enabled      bool     `json:"enabled" bson:"enabled"`
}

type AvailableTopic struct {
	BaseModel `bson:",inline"`
	Ident     string   `json:"ident" bson:"ident" required:"true"`
	AppName   string   `json:"app_name" bson:"app_name" required:"true"`
	Channels  []string `json:"channels" bson:"channels" required:"true"`
}

type SentItem struct {
	BaseModel      `bson:",inline"`
	CreatedBy      string                 `json:"created_by" bson:"created_by" required:"true"`
	Organization   string                 `json:"org" bson:"org" required:"true"`
	AppName        string                 `json:"app_name" bson:"app_name" required:"true"`
	Topic          string                 `json:"topic" bson:"topic" required:"true"`
	User           string                 `json:"user" bson:"user" required:"true"`
	DestinationUri string                 `json:"destination_uri" bson:"destination_uri" required:"true"`
	Text           string                 `json:"text" bson:"text" required:"true"`
	IsRead         bool                   `json:"is_read" bson:"is_read"`
	Context        map[string]interface{} `json:"context" bson:"context"`
	Entity         string                 `json:"entity" bson:"entity" required:"true"`
}

func (self *SentItem) IsValid() bool {
	if len(self.Text) == 0 {
		return false
	}
	return true
}

type SavedItem struct {
	BaseModel `bson:",inline"`
	Data      interface{} `bson:"data"`
	Details   PendingItem `bson:"details"`
}

type PendingItem struct {
	BaseModel      `bson:",inline"`
	CreatedBy      string                 `json:"created_by" bson:"created_by" required:"true"`
	Organization   string                 `json:"org" bson:"org" required:"true"`
	AppName        string                 `json:"app_name" bson:"app_name" required:"true"`
	Topic          string                 `json:"topic" bson:"topic" required:"true"`
	IsPending      bool                   `json:"is_pending" bson:"is_pending" required:"true"`
	User           string                 `json:"user" bson:"user" required:"true"`
	DestinationUri string                 `json:"destination_uri" bson:"destination_uri" required:"true"`
	Context        map[string]interface{} `json:"context" bson:"context" required:"true"`
	IsRead         bool                   `json:"is_read" bson:"is_read"`
	Entity         string                 `json:"entity" bson:"entity" required:"true"`
}

func (self *PendingItem) IsValid() bool {
	if len(self.Context) == 0 {
		return false
	}
	return true
}

type LastSeen struct {
	BaseModel    `bson:",inline"`
	User         string `json:"user" bson:"user" required:"true"`
	Organization string `json:"org" bson:"org"`
	AppName      string `json:"app_name" bson:"app_name"`
	Timestamp    int64  `json:"timestamp" bson:"timestamp" required:"true"`
}

type Gully struct {
	BaseModel    `bson:",inline"`
	User         string                 `json:"user" bson:"user"`
	Organization string                 `json:"org" bson:"org"`
	AppName      string                 `json:"app_name" bson:"app_name"`
	Data         map[string]interface{} `json:"data" bson:"data" required:"true"`
	Ident        string                 `json:"ident" bson:"ident" required:"true"`
}

func (self *Gully) IsValid(op_type int) bool {

	if len(self.Ident) == 0 {
		return false
	}

	if op_type == INSERT_OPERATION {
		if len(self.Data) == 0 {
			return false
		}
	}

	return true
}

type UserLocale struct {
	BaseModel `bson:",inline"`
	User      string `json:"user" bson:"user" required:"true"`
	Locale    string `json:"locale" bson:"locale" required:"true"`
	TimeZone  string `json:"timezone" bson:"timezone" required:"true"`
}

func (self *UserLocale) IsValid() bool {
	if len(self.Locale) == 0 || len(self.User) == 0 || len(self.TimeZone) == 0 {
		return false
	}
	return true
}

type Channel struct {
	Name    string `json:"name" bson:"name"`
	Enabled bool   `json:"enabled" bson:"enabled"`
	Default bool   `json:"default" bson:"default"`
	Locked  bool   `json:"locked" bson:"locked"`
}

type Topic struct {
	BaseModel    `bson:",inline"`
	User         string    `json:"user" bson:"user"`
	Organization string    `json:"org" bson:"org"`
	Channels     []Channel `json:"channels" bson:"channels" required:"true"`
	Ident        string    `json:"ident" bson:"ident" required:"true"`
}
<commit_msg>add bindings to form<commit_after>package db

const BLANK = ""

const (
	DELETE_OPERATION = 1
	INSERT_OPERATION = 2
	UPDATE_OPERATION = 3

	SentCollection       = "sent_notifications"
	StatsCollection      = "last_seen_at"
	TopicCollection      = "topics"
	GullyCollection      = "gullys"
	UserLocaleCollection = "user_locales"

	SavedEmailCollection     = "saved_email"
	SavedPushCollection      = "saved_push"
	SavedWebCollection       = "saved_web"
	AvailableTopicCollection = "topics_available"

	DefaultsCollection  = "defaults"
	LocksCollection     = "locks"
	ProcessedCollection = "processed"
)

type Processed struct {
	BaseModel    `bson:",inline"`
	User         string `bson:"user"`
	Organization string `bson:"org"`
}

type Defaults struct {
	BaseModel    `bson:",inline"`
	Organization string   `json:"org" bson:"org"`
	Topic        string   `json:"ident" bson:"ident" required:"true"`
	Channels     []string `json:"channels" bson:"channels" required:"true"`
	Enabled      bool     `json:"enabled" bson:"enabled"`
}

type Locks struct {
	BaseModel    `bson:",inline"`
	Organization string   `json:"org" bson:"org" required:"true"`
	Topic        string   `json:"ident" bson:"ident" required:"true"`
	Channels     []string `json:"channels" bson:"channels" required:"true"`
	Enabled      bool     `json:"enabled" bson:"enabled"`
}

type AvailableTopic struct {
	BaseModel `bson:",inline"`
	Ident     string   `json:"ident" bson:"ident" required:"true" form:"ident" binding:"required"`
	AppName   string   `json:"app_name" bson:"app_name" required:"true" form:"app_name" binding:"required"`
	Channels  []string `json:"channels" bson:"channels" required:"true" form:"channels" binding:"required"`
}

type SentItem struct {
	BaseModel      `bson:",inline"`
	CreatedBy      string                 `json:"created_by" bson:"created_by" required:"true"`
	Organization   string                 `json:"org" bson:"org" required:"true"`
	AppName        string                 `json:"app_name" bson:"app_name" required:"true"`
	Topic          string                 `json:"topic" bson:"topic" required:"true"`
	User           string                 `json:"user" bson:"user" required:"true"`
	DestinationUri string                 `json:"destination_uri" bson:"destination_uri" required:"true"`
	Text           string                 `json:"text" bson:"text" required:"true"`
	IsRead         bool                   `json:"is_read" bson:"is_read"`
	Context        map[string]interface{} `json:"context" bson:"context"`
	Entity         string                 `json:"entity" bson:"entity" required:"true"`
}

func (self *SentItem) IsValid() bool {
	if len(self.Text) == 0 {
		return false
	}
	return true
}

type SavedItem struct {
	BaseModel `bson:",inline"`
	Data      interface{} `bson:"data"`
	Details   PendingItem `bson:"details"`
}

type PendingItem struct {
	BaseModel      `bson:",inline"`
	CreatedBy      string                 `json:"created_by" bson:"created_by" required:"true"`
	Organization   string                 `json:"org" bson:"org" required:"true"`
	AppName        string                 `json:"app_name" bson:"app_name" required:"true"`
	Topic          string                 `json:"topic" bson:"topic" required:"true"`
	IsPending      bool                   `json:"is_pending" bson:"is_pending" required:"true"`
	User           string                 `json:"user" bson:"user" required:"true"`
	DestinationUri string                 `json:"destination_uri" bson:"destination_uri" required:"true"`
	Context        map[string]interface{} `json:"context" bson:"context" required:"true"`
	IsRead         bool                   `json:"is_read" bson:"is_read"`
	Entity         string                 `json:"entity" bson:"entity" required:"true"`
}

func (self *PendingItem) IsValid() bool {
	if len(self.Context) == 0 {
		return false
	}
	return true
}

type LastSeen struct {
	BaseModel    `bson:",inline"`
	User         string `json:"user" bson:"user" required:"true"`
	Organization string `json:"org" bson:"org"`
	AppName      string `json:"app_name" bson:"app_name"`
	Timestamp    int64  `json:"timestamp" bson:"timestamp" required:"true"`
}

type Gully struct {
	BaseModel    `bson:",inline"`
	User         string                 `json:"user" bson:"user"`
	Organization string                 `json:"org" bson:"org"`
	AppName      string                 `json:"app_name" bson:"app_name"`
	Data         map[string]interface{} `json:"data" bson:"data" required:"true"`
	Ident        string                 `json:"ident" bson:"ident" required:"true"`
}

func (self *Gully) IsValid(op_type int) bool {

	if len(self.Ident) == 0 {
		return false
	}

	if op_type == INSERT_OPERATION {
		if len(self.Data) == 0 {
			return false
		}
	}

	return true
}

type UserLocale struct {
	BaseModel `bson:",inline"`
	User      string `json:"user" bson:"user" required:"true"`
	Locale    string `json:"locale" bson:"locale" required:"true"`
	TimeZone  string `json:"timezone" bson:"timezone" required:"true"`
}

func (self *UserLocale) IsValid() bool {
	if len(self.Locale) == 0 || len(self.User) == 0 || len(self.TimeZone) == 0 {
		return false
	}
	return true
}

type Channel struct {
	Name    string `json:"name" bson:"name"`
	Enabled bool   `json:"enabled" bson:"enabled"`
	Default bool   `json:"default" bson:"default"`
	Locked  bool   `json:"locked" bson:"locked"`
}

type Topic struct {
	BaseModel    `bson:",inline"`
	User         string    `json:"user" bson:"user"`
	Organization string    `json:"org" bson:"org"`
	Channels     []Channel `json:"channels" bson:"channels" required:"true"`
	Ident        string    `json:"ident" bson:"ident" required:"true" form:"ident" binding:"required"`
}
<|endoftext|>


<commit_before>package db

import (
	"database/sql/driver"
	"fmt"
	"log"
	"reflect"
	"strings"
)

type Qarray []interface{}

func NewQarrayText(text string) (Qarray, error) {
	if strings.HasPrefix(text, "{") && strings.HasSuffix(text, "}") {
		s := strings.Trim(text, "{}")
		a := strings.Split(s, ",")
		return NewQarray(a)
	}

	log.Println("invalid Qarray format")
	return Qarray{}, nil
}

func NewQarray(a []string) (Qarray, error) {
	q := make(Qarray, len(a))
	for i := 0; i < len(a); i++ {
		q[i] = a[i]
	}
	return q, nil
}

func (q Qarray) String() string {
	if len(q) == 0 {
		return "{}"
	}

	var s = make([]string, len(q))
	r := strings.NewReplacer("\\", "\\\\", "'", "''", "\"", "\\\"")

	for i, v := range q {
		sv := reflect.ValueOf(v)
		switch sv.Kind() {
		default:
			s[i] = "\"" + r.Replace(fmt.Sprintf("%s", v)) + "\""
			break
		case reflect.Bool,
			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
			reflect.Float32, reflect.Float64:
			s[i] = fmt.Sprintf("%v", v)
			break
		}
	}
	return "{" + strings.Join(s, ",") + "}"
}

// driver.Valuer for sql value save
func (q Qarray) Value() (driver.Value, error) {
	return q.String(), nil
}

// driver.Scanner for sql value load
func (q *Qarray) Scan(src interface{}) (err error) {
	switch s := src.(type) {
	case string:
		*q, err = NewQarrayText(s)
		return
	case []byte:
		*q, err = NewQarrayText(string(s))
		return
	case []interface{}:
		*q = Qarray(s)
		return
	}
	return
}

// Index returns the index of the string s in Qarray, or -1 if s is not found.
func (q Qarray) Index(s string) int {
	for i, v := range q {
		if vs := v.(string); s == vs {
			return i
		}
		if s == fmt.Sprint(v) {
			return i
		}
	}
	return -1
}

// Contains returns true if the string s is found
func (q Qarray) Contains(s string) bool {
	return q.Index(s) > -1
}

func (q Qarray) ToStringSlice() []string {
	a := make([]string, len(q))
	for i, v := range q {
		a[i] = fmt.Sprint(v)
	}
	return a
}
<commit_msg>fixes Qarray build function: NewQarrayText()<commit_after>package db

import (
	"database/sql/driver"
	"fmt"
	// "log"
	"reflect"
	"strings"
)

type Qarray []interface{}

func NewQarrayText(s string) (Qarray, error) {
	if strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}") {
		s = strings.Trim(s, "{}")
	}
	if len(s) == 0 {
		return Qarray{}, nil
	}
	a := strings.Split(s, ",")
	return NewQarray(a)

	// log.Println("invalid Qarray format")
	// return Qarray{}, nil
}

func NewQarray(a []string) (Qarray, error) {
	q := make(Qarray, len(a))
	for i := 0; i < len(a); i++ {
		q[i] = a[i]
	}
	return q, nil
}

func (q Qarray) String() string {
	if len(q) == 0 {
		return "{}"
	}

	var s = make([]string, len(q))
	r := strings.NewReplacer("\\", "\\\\", "'", "''", "\"", "\\\"")

	for i, v := range q {
		sv := reflect.ValueOf(v)
		switch sv.Kind() {
		default:
			s[i] = "\"" + r.Replace(fmt.Sprintf("%s", v)) + "\""
			break
		case reflect.Bool,
			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
			reflect.Float32, reflect.Float64:
			s[i] = fmt.Sprintf("%v", v)
			break
		}
	}
	return "{" + strings.Join(s, ",") + "}"
}

// driver.Valuer for sql value save
func (q Qarray) Value() (driver.Value, error) {
	return q.String(), nil
}

// driver.Scanner for sql value load
func (q *Qarray) Scan(src interface{}) (err error) {
	switch s := src.(type) {
	case string:
		*q, err = NewQarrayText(s)
		return
	case []byte:
		*q, err = NewQarrayText(string(s))
		return
	case []interface{}:
		*q = Qarray(s)
		return
	}
	return
}

// Index returns the index of the string s in Qarray, or -1 if s is not found.
func (q Qarray) Index(s string) int {
	for i, v := range q {
		if vs := v.(string); s == vs {
			return i
		}
		if s == fmt.Sprint(v) {
			return i
		}
	}
	return -1
}

// Contains returns true if the string s is found
func (q Qarray) Contains(s string) bool {
	return q.Index(s) > -1
}

func (q Qarray) ToStringSlice() []string {
	a := make([]string, len(q))
	for i, v := range q {
		a[i] = fmt.Sprint(v)
	}
	return a
}
<|endoftext|>


<commit_before>package DatabaseConnector

import "time"

var WorkerQueue chan chan WorkRequest
var workers chan Worker
var stop bool

func stopWorker() bool {
	select {
	case w, ok := <-workers:
		if ok {
			w.stop()
			return true
		} else {
			//channel workers is nil / not initialized
			return false
		}
	default:
		//No workers left
		return false
	}
}

func stopDispatcher() {
	defer close(workers)
	for stopWorker() {
	}
	stop = true
}

func startDispatcher(nworkers int) {
	// First, initialize the channel we are going to put the workers' work channels into.
	WorkerQueue = make(chan chan WorkRequest, nworkers)
	workers = make(chan Worker, nworkers)
	// Now, create all of our workers.
	for i := 0; i < nworkers; i++ {
		//fmt.Println("Starting worker", i+1)
		worker := NewWorker(i+1, WorkerQueue)
		worker.start()
		workers <- worker
	}

	stop = false

	go func() {
		for {

			select {
			case work := <-WorkQueue:
				//fmt.Println("Received work requeust")
				go func() {
					worker := <-WorkerQueue
					//fmt.Println("Dispatching work request")
					worker <- work
				}()
			default:
				if stop == true {
					break
				}
				time.Sleep(10 * time.Millisecond) //Sleep a bit when there is no work. performance reasons.
			}
		}
	}()
}
<commit_msg>Changed order of logic. Prevented unnessary loops.<commit_after>package DatabaseConnector

import "time"

var WorkerQueue chan chan WorkRequest
var workers chan Worker
var stop bool

func stopWorker() bool {
	select {
	case w, ok := <-workers:
		if ok {
			w.stop()
			return true
		} else {
			//channel workers is nil / not initialized
			return false
		}
	default:
		//No workers left
		return false
	}
}

func stopDispatcher() {
	defer close(workers)
	stop = true
	for stopWorker() {
	}
}

func startDispatcher(nworkers int) {
	// First, initialize the channel we are going to put the workers' work channels into.
	WorkerQueue = make(chan chan WorkRequest, nworkers)
	workers = make(chan Worker, nworkers)
	// Now, create all of our workers.
	for i := 0; i < nworkers; i++ {
		//fmt.Println("Starting worker", i+1)
		worker := NewWorker(i+1, WorkerQueue)
		worker.start()
		workers <- worker
	}

	stop = false

	go func() {
		for {

			select {
			case work := <-WorkQueue:
				//fmt.Println("Received work requeust")
				go func() {
					worker := <-WorkerQueue
					//fmt.Println("Dispatching work request")
					worker <- work
				}()
			default:
				if stop == true {
					break
				}
				time.Sleep(10 * time.Millisecond) //Sleep a bit when there is no work. performance reasons.
			}
		}
	}()
}
<|endoftext|>


<commit_before>package sudoku

import (
	"bytes"
	"fmt"
)

// Return this map of values as a formatted 2-D grid string.
func Display(values map[string]string, err error) string {
	if err != nil {
      return fmt.Sprintf("Sorry cannot solve the Sudoku here's why: %v", err)
	}

	var buffer bytes.Buffer
	for _, row := range rows {
		for _, col := range cols {
			buffer.WriteString(fmt.Sprintf("%v ", values[string(row)+string(col)]))
		}
		buffer.WriteString("\n")
	}
  fmt.Println(len(buffer.String()))
	return buffer.String()
}
<commit_msg>Removed space from the display of the puzzle<commit_after>package sudoku

import (
	"bytes"
	"fmt"
)

// Return this map of values as a formatted 2-D grid string.
func Display(values map[string]string, err error) string {
	if err != nil {
      return fmt.Sprintf("Sorry cannot solve the Sudoku here's why: %v", err)
	}

	var buffer bytes.Buffer
	for _, row := range rows {
		for _, col := range cols {
			buffer.WriteString(fmt.Sprintf("%v", values[string(row)+string(col)]))
		}
		buffer.WriteString("\n")
	}
	return buffer.String()
}
<|endoftext|>


<commit_before>package victor_test

import (
    "github.com/brettbuddin/victor"
    "testing"
)

func TestHear(t *testing.T) {
    doesHear(t, "d[eu]rp", "derp")
    doesHear(t, "d[eu]rp", "durp")

    doesNotHear(t, "hello$", "hello nope")
    doesNotHear(t, "^hello", "nope hello")
}

func TestRespond(t *testing.T) {
    doesRespond(t, "d[eu]rp", "robot: derp")
    doesRespond(t, "d[eu]rp", "robot durp")
    doesRespond(t, "d[eu]rp", "robot, durp")
    doesRespond(t, "d[eu]rp$", "robot, durp")
    doesRespond(t, "d[eu]rp$", "Robot, durp")
}

func TestRememberUser(t *testing.T) {
    brain := victor.NewBrain("robot")
    user := &victor.User{Id: 1, Name: "brett"}

    brain.RememberUser(user)

    users := brain.KnownUsers()

    found := false

    for _, u := range users {
        if u == user {
            found = true
        }
    }

    if found == false {
        t.Error("did not remember user")
    }
}

func doesRespond(t *testing.T, pattern, messageText string) {
    if testRespond(pattern, messageText) == false {
        t.Errorf("%s not triggered by %s", pattern, messageText)
    }
}

func doesNotRespond(t *testing.T, pattern, messageText string) {
    if testRespond(pattern, messageText) {
        t.Errorf("%s triggered by %s", pattern, messageText)
    }
}

func doesHear(t *testing.T, pattern, messageText string) {
    if testHear(pattern, messageText) == false {
        t.Errorf("%s not triggered by %s", pattern, messageText)
    }
}

func doesNotHear(t *testing.T, pattern, messageText string) {
    if testHear(pattern, messageText) {
        t.Errorf("%s triggered by %s", pattern, messageText)
    }
}

func testRespond(pattern, messageText string) bool {
    brain := victor.NewBrain("robot")
    triggered := false

    brain.Respond(pattern, func(msg *victor.TextMessage) {
        triggered = true
    })

    msg := &victor.TextMessage{
        Id:   1,
        Body: messageText,
    }

    brain.Receive(msg)

    return triggered
}

func testHear(pattern, messageText string) bool {
    brain := victor.NewBrain("robot")
    triggered := false

    brain.Hear(pattern, func(msg *victor.TextMessage) {
        triggered = true
    })

    msg := &victor.TextMessage{
        Id:   1,
        Body: messageText,
    }

    brain.Receive(msg)

    return triggered
}
<commit_msg>Yeah... not right now.<commit_after>package victor_test

import (
    "github.com/brettbuddin/victor"
    "testing"
)

func TestHear(t *testing.T) {
    doesHear(t, "d[eu]rp", "derp")
    doesHear(t, "d[eu]rp", "durp")

    doesNotHear(t, "hello$", "hello nope")
    doesNotHear(t, "^hello", "nope hello")
}

func TestRespond(t *testing.T) {
    doesRespond(t, "d[eu]rp", "robot: derp")
    doesRespond(t, "d[eu]rp", "robot durp")
    doesRespond(t, "d[eu]rp", "robot, durp")
    doesRespond(t, "d[eu]rp$", "robot, durp")
    doesRespond(t, "d[eu]rp$", "Robot, durp")
}

func doesRespond(t *testing.T, pattern, messageText string) {
    if testRespond(pattern, messageText) == false {
        t.Errorf("%s not triggered by %s", pattern, messageText)
    }
}

func doesNotRespond(t *testing.T, pattern, messageText string) {
    if testRespond(pattern, messageText) {
        t.Errorf("%s triggered by %s", pattern, messageText)
    }
}

func doesHear(t *testing.T, pattern, messageText string) {
    if testHear(pattern, messageText) == false {
        t.Errorf("%s not triggered by %s", pattern, messageText)
    }
}

func doesNotHear(t *testing.T, pattern, messageText string) {
    if testHear(pattern, messageText) {
        t.Errorf("%s triggered by %s", pattern, messageText)
    }
}

func testRespond(pattern, messageText string) bool {
    brain := victor.NewBrain("robot")
    triggered := false

    brain.Respond(pattern, func(msg *victor.TextMessage) {
        triggered = true
    })

    msg := &victor.TextMessage{
        Id:   1,
        Body: messageText,
    }

    brain.Receive(msg)

    return triggered
}

func testHear(pattern, messageText string) bool {
    brain := victor.NewBrain("robot")
    triggered := false

    brain.Hear(pattern, func(msg *victor.TextMessage) {
        triggered = true
    })

    msg := &victor.TextMessage{
        Id:   1,
        Body: messageText,
    }

    brain.Receive(msg)

    return triggered
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/Comcast/webpa-common/health"
	"github.com/Comcast/webpa-common/logging"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
)

// Begin mock declarations

// mockHandler only needs to mock the `HandleRequest` method
type mockHandler struct {
	mock.Mock
}

func (m *mockHandler) HandleRequest(workerID int, inRequest CaduceusRequest) {
	m.Called()
	return
}

// mockHealthTracker needs to mock things from both the `HealthTracker`
// interface as well as the `health.Monitor` interface
type mockHealthTracker struct {
	mock.Mock
}

func (m *mockHealthTracker) SendEvent(healthFunc health.HealthFunc) {
	m.Called(healthFunc)
	return
}

func (m *mockHealthTracker) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	m.Called(response, request)
	return
}

func (m *mockHealthTracker) IncrementBucket(inSize int) {
	return
}

// Begin test functions

func TestMain(m *testing.M) {
	os.Exit(m.Run())
}

func TestWorkerPool(t *testing.T) {
	assert := assert.New(t)

	workerPool := WorkerPoolFactory{
		NumWorkers: 1,
		QueueSize:  1,
	}.New()

	t.Run("TestWorkerPoolCreation", func(t *testing.T) {
		assert.NotNil(workerPool)
	})

	t.Run("TestWorkerPoolSend", func(t *testing.T) {
		err := workerPool.Send(func(workerID int) {
			// do nothing
		})

		assert.Nil(err)
	})

	workerPool = WorkerPoolFactory{
		NumWorkers: 1,
		QueueSize:  0,
	}.New()

	t.Run("TestWorkerPoolFullQueue", func(t *testing.T) {
		err := workerPool.Send(func(workerID int) {
			// do nothing
		})

		assert.NotNil(err)
	})
}

func TestIncrementBucket(t *testing.T) {
	assert := assert.New(t)

	testData := []struct {
		inSize       int
		expectedStat health.Stat
	}{
		{inSize: 99, expectedStat: PayloadsOverZero},
		{inSize: 999, expectedStat: PayloadsOverHundred},
		{inSize: 9999, expectedStat: PayloadsOverThousand},
		{inSize: 10001, expectedStat: PayloadsOverTenThousand},
	}

	t.Run("TestIncrementBucket", func(t *testing.T) {
		for _, data := range testData {
			fakeMonitor := new(mockHealthTracker)
			fakeMonitor.On("SendEvent", mock.AnythingOfType("health.HealthFunc")).Run(
				func(args mock.Arguments) {
					healthFunc := args.Get(0).(health.HealthFunc)
					stats := make(health.Stats)

					healthFunc(stats)
					assert.Equal(1, stats[data.expectedStat])
				}).Once()

			caduceusHealth := &CaduceusHealth{fakeMonitor}

			caduceusHealth.IncrementBucket(data.inSize)
			fakeMonitor.AssertExpectations(t)
		}
	})
}

func TestServeHTTP(t *testing.T) {
	assert := assert.New(t)

	logger := logging.DefaultLogger()
	fakeHandler := new(mockHandler)
	fakeHealth := new(mockHealthTracker)

	requestSuccessful := func(func(workerID int)) error {
		return nil
	}

	serverWrapper := &ServerHandler{
		Logger:          logger,
		caduceusHandler: fakeHandler,
		caduceusHealth:  fakeHealth,
		doJob:           requestSuccessful,
	}

	req := httptest.NewRequest("POST", "localhost:8080", strings.NewReader("Test payload."))

	t.Run("TestHappyPath", func(t *testing.T) {
		req.Header.Set("Content-Type", "text/plain")

		w := httptest.NewRecorder()
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(202, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})

	t.Run("TestTooManyHeaders", func(t *testing.T) {
		req.Header.Add("Content-Type", "too/many/headers")

		w := httptest.NewRecorder()
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(400, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})

	t.Run("TestWrongHeader", func(t *testing.T) {
		req.Header.Del("Content-Type")

		w := httptest.NewRecorder()
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(400, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})

	t.Run("TestFullQueue", func(t *testing.T) {
		req.Header.Set("Content-Type", "text/plain")

		w := httptest.NewRecorder()
		requestTimeout := func(func(workerID int)) error {
			return errors.New("Intentional error.")
		}
		serverWrapper.doJob = requestTimeout
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(408, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})
}
<commit_msg>Finished first iteration of tests and going to run past John now.<commit_after>package main

import (
	"errors"
	"github.com/Comcast/webpa-common/health"
	"github.com/Comcast/webpa-common/logging"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"net/http"
	"net/http/httptest"
	"os"
	"strings"
	"testing"
)

// Begin mock declarations

// mockHandler only needs to mock the `HandleRequest` method
type mockHandler struct {
	mock.Mock
}

func (m *mockHandler) HandleRequest(workerID int, inRequest CaduceusRequest) {
	m.Called(workerID, inRequest)
	return
}

// mockHealthTracker needs to mock things from both the `HealthTracker`
// interface as well as the `health.Monitor` interface
type mockHealthTracker struct {
	mock.Mock
}

func (m *mockHealthTracker) SendEvent(healthFunc health.HealthFunc) {
	m.Called(healthFunc)
	return
}

func (m *mockHealthTracker) ServeHTTP(response http.ResponseWriter, request *http.Request) {
	m.Called(response, request)
	return
}

func (m *mockHealthTracker) IncrementBucket(inSize int) {
	m.Called(inSize)
	return
}

// Begin test functions

func TestMain(m *testing.M) {
	os.Exit(m.Run())
}

func TestWorkerPool(t *testing.T) {
	assert := assert.New(t)

	workerPool := WorkerPoolFactory{
		NumWorkers: 1,
		QueueSize:  1,
	}.New()

	t.Run("TestWorkerPoolCreation", func(t *testing.T) {
		assert.NotNil(workerPool)
	})

	t.Run("TestWorkerPoolSend", func(t *testing.T) {
		err := workerPool.Send(func(workerID int) {
			// do nothing
		})

		assert.Nil(err)
	})

	workerPool = WorkerPoolFactory{
		NumWorkers: 1,
		QueueSize:  0,
	}.New()

	t.Run("TestWorkerPoolFullQueue", func(t *testing.T) {
		err := workerPool.Send(func(workerID int) {
			// do nothing
		})

		assert.NotNil(err)
	})
}

func TestIncrementBucket(t *testing.T) {
	assert := assert.New(t)

	testData := []struct {
		inSize       int
		expectedStat health.Stat
	}{
		{inSize: 99, expectedStat: PayloadsOverZero},
		{inSize: 999, expectedStat: PayloadsOverHundred},
		{inSize: 9999, expectedStat: PayloadsOverThousand},
		{inSize: 10001, expectedStat: PayloadsOverTenThousand},
	}

	t.Run("TestIncrementBucket", func(t *testing.T) {
		for _, data := range testData {
			fakeMonitor := new(mockHealthTracker)
			fakeMonitor.On("SendEvent", mock.AnythingOfType("health.HealthFunc")).Run(
				func(args mock.Arguments) {
					healthFunc := args.Get(0).(health.HealthFunc)
					stats := make(health.Stats)

					healthFunc(stats)
					assert.Equal(1, stats[data.expectedStat])
				}).Once()

			caduceusHealth := &CaduceusHealth{fakeMonitor}

			caduceusHealth.IncrementBucket(data.inSize)
			fakeMonitor.AssertExpectations(t)
		}
	})
}

func TestServeHTTP(t *testing.T) {
	assert := assert.New(t)

	logger := logging.DefaultLogger()
	fakeHandler := new(mockHandler)
	fakeHandler.On("HandleRequest", mock.AnythingOfType("int"), mock.AnythingOfType("CaduceusRequest")).Return().Once()

	fakeHealth := new(mockHealthTracker)
	fakeHealth.On("IncrementBucket", mock.AnythingOfType("int")).Return().Once()

	requestSuccessful := func(func(workerID int)) error {
		fakeHandler.HandleRequest(0, CaduceusRequest{})
		return nil
	}

	serverWrapper := &ServerHandler{
		Logger:          logger,
		caduceusHandler: fakeHandler,
		caduceusHealth:  fakeHealth,
		doJob:           requestSuccessful,
	}

	req := httptest.NewRequest("POST", "localhost:8080", strings.NewReader("Test payload."))

	t.Run("TestHappyPath", func(t *testing.T) {
		req.Header.Set("Content-Type", "text/plain")

		w := httptest.NewRecorder()
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(202, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})

	t.Run("TestTooManyHeaders", func(t *testing.T) {
		req.Header.Add("Content-Type", "too/many/headers")

		w := httptest.NewRecorder()
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(400, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})

	t.Run("TestWrongHeader", func(t *testing.T) {
		req.Header.Del("Content-Type")

		w := httptest.NewRecorder()
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(400, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})

	t.Run("TestFullQueue", func(t *testing.T) {
		req.Header.Set("Content-Type", "text/plain")

		w := httptest.NewRecorder()
		requestTimeout := func(func(workerID int)) error {
			return errors.New("Intentional error.")
		}
		serverWrapper.doJob = requestTimeout
		serverWrapper.ServeHTTP(w, req)
		resp := w.Result()

		assert.Equal(408, resp.StatusCode)
		fakeHandler.AssertExpectations(t)
		fakeHealth.AssertExpectations(t)
	})
}
<|endoftext|>


<commit_before>package radix

import (
	"io"
	"sync"
	. "testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/mediocregopher/radix/v3/trace"
)

func testPool(size int, opts ...PoolOpt) *Pool {
	pool, err := NewPool("tcp", "localhost:6379", size, opts...)
	if err != nil {
		panic(err)
	}
	<-pool.initDone
	return pool
}

func TestPool(t *T) {
	testEcho := func(c Conn) error {
		exp := randStr()
		var out string
		assert.Nil(t, c.Do(Cmd(&out, "ECHO", exp)))
		assert.Equal(t, exp, out)
		return nil
	}

	do := func(opts ...PoolOpt) {
		opts = append(opts, PoolOnFullClose())
		size := 10
		pool := testPool(size, opts...)
		var wg sync.WaitGroup
		for i := 0; i < size*4; i++ {
			wg.Add(1)
			go func() {
				for i := 0; i < 100; i++ {
					assert.NoError(t, pool.Do(WithConn("", testEcho)))
				}
				wg.Done()
			}()
		}
		wg.Wait()
		assert.Equal(t, size, pool.NumAvailConns())
		pool.Close()
		assert.Equal(t, 0, pool.NumAvailConns())
	}

	t.Run("onEmptyWait", func(t *T) { do(PoolOnEmptyWait()) })
	t.Run("onEmptyCreate", func(t *T) { do(PoolOnEmptyCreateAfter(0)) })
	t.Run("onEmptyCreateAfter", func(t *T) { do(PoolOnEmptyCreateAfter(1 * time.Second)) })
	// This one is expected to error, since this test empties the pool by design
	//t.Run("onEmptyErr", func(t *T) { do(PoolOnEmptyErrAfter(0)) })
	t.Run("onEmptyErrAfter", func(t *T) { do(PoolOnEmptyErrAfter(1 * time.Second)) })

	t.Run("withTrace", func(t *T) {
		var connCreatedCount int
		var connClosedCount int
		var doCompletedCount int
		pt := trace.PoolTrace{
			ConnCreated: func(done trace.PoolConnCreated) {
				connCreatedCount++
			},
			ConnClosed: func(closed trace.PoolConnClosed) {
				connClosedCount++
			},
			DoCompleted: func(completed trace.PoolDoCompleted) {
				doCompletedCount++
			},
		}
		do(PoolWithTrace(pt))
		if connCreatedCount != connClosedCount {
			t.Fail()
		}
		if doCompletedCount == 0 {
			t.Fail()
		}
	})
}

// Test all the different OnEmpty behaviors
func TestPoolGet(t *T) {
	getBlock := func(p *Pool) (time.Duration, error) {
		start := time.Now()
		_, err := p.get()
		return time.Since(start), err
	}

	// this one is a bit weird, cause it would block infinitely if we let it
	t.Run("onEmptyWait", func(t *T) {
		pool := testPool(1, PoolOnEmptyWait())
		conn, err := pool.get()
		assert.NoError(t, err)

		go func() {
			time.Sleep(2 * time.Second)
			pool.put(conn)
		}()
		took, err := getBlock(pool)
		assert.NoError(t, err)
		assert.True(t, took-2*time.Second < 20*time.Millisecond)
	})

	// the rest are pretty straightforward
	gen := func(mkOpt func(time.Duration) PoolOpt, d time.Duration, expErr error) func(*T) {
		return func(t *T) {
			pool := testPool(0, PoolOnFullClose(), mkOpt(d))
			took, err := getBlock(pool)
			assert.Equal(t, expErr, err)
			assert.True(t, took-d < 20*time.Millisecond)
		}
	}

	t.Run("onEmptyCreate", gen(PoolOnEmptyCreateAfter, 0, nil))
	t.Run("onEmptyCreateAfter", gen(PoolOnEmptyCreateAfter, 1*time.Second, nil))
	t.Run("onEmptyErr", gen(PoolOnEmptyErrAfter, 0, ErrPoolEmpty))
	t.Run("onEmptyErrAfter", gen(PoolOnEmptyErrAfter, 1*time.Second, ErrPoolEmpty))
}

func TestPoolOnFull(t *T) {
	t.Run("onFullClose", func(t *T) {
		pool := testPool(1, PoolOnFullClose())
		defer pool.Close()
		assert.Equal(t, 1, len(pool.pool))

		spc, err := pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 1, len(pool.pool))
	})

	t.Run("onFullBuffer", func(t *T) {
		pool := testPool(1, PoolOnFullBuffer(1, 1*time.Second))
		defer pool.Close()
		assert.Equal(t, 1, len(pool.pool))

		// putting a conn should overflow
		spc, err := pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 2, len(pool.pool))

		// another shouldn't, overflow is full
		spc, err = pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 2, len(pool.pool))

		// retrieve from the pool, drain shouldn't do anything because the
		// overflow is empty now
		<-pool.pool
		assert.Equal(t, 1, len(pool.pool))
		time.Sleep(2 * time.Second)
		assert.Equal(t, 1, len(pool.pool))

		// if both are full then drain should remove the overflow one
		spc, err = pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 2, len(pool.pool))
		time.Sleep(2 * time.Second)
		assert.Equal(t, 1, len(pool.pool))
	})
}

func TestPoolPut(t *T) {
	size := 10
	pool := testPool(size)

	assertPoolConns := func(exp int) {
		assert.Equal(t, exp, pool.NumAvailConns())
	}
	assertPoolConns(10)

	// Make sure that put does not accept a connection which has had a critical
	// network error
	pool.Do(WithConn("", func(conn Conn) error {
		assertPoolConns(9)
		conn.(*ioErrConn).lastIOErr = io.EOF
		return nil
	}))
	assertPoolConns(9)

	// Make sure that a put _does_ accept a connection which had a
	// marshal/unmarshal error
	pool.Do(WithConn("", func(conn Conn) error {
		assert.NotNil(t, conn.Do(FlatCmd(nil, "ECHO", "", func() {})))
		assert.Nil(t, conn.(*ioErrConn).lastIOErr)
		return nil
	}))
	assertPoolConns(9)

	// Make sure that a put _does_ accept a connection which had an app level
	// resp error
	pool.Do(WithConn("", func(conn Conn) error {
		assert.NotNil(t, Cmd(nil, "CMDDNE"))
		assert.Nil(t, conn.(*ioErrConn).lastIOErr)
		return nil
	}))
	assertPoolConns(9)

	// Make sure that closing the pool closes outstanding connections as well
	closeCh := make(chan bool)
	go func() {
		<-closeCh
		assert.Nil(t, pool.Close())
		closeCh <- true
	}()
	pool.Do(WithConn("", func(conn Conn) error {
		closeCh <- true
		<-closeCh
		return nil
	}))
	assertPoolConns(0)
}

func TestPoolClose(t *T) {
	pool := testPool(1)
	assert.NoError(t, pool.Do(Cmd(nil, "PING")))
	assert.NoError(t, pool.Close())
	assert.Error(t, errClientClosed, pool.Do(Cmd(nil, "PING")))
}
<commit_msg>Fix race condition for DoCompletedCount<commit_after>package radix

import (
	"io"
	"sync"
	"sync/atomic"
	. "testing"
	"time"

	"github.com/stretchr/testify/assert"

	"github.com/mediocregopher/radix/v3/trace"
)

func testPool(size int, opts ...PoolOpt) *Pool {
	pool, err := NewPool("tcp", "localhost:6379", size, opts...)
	if err != nil {
		panic(err)
	}
	<-pool.initDone
	return pool
}

func TestPool(t *T) {
	testEcho := func(c Conn) error {
		exp := randStr()
		var out string
		assert.Nil(t, c.Do(Cmd(&out, "ECHO", exp)))
		assert.Equal(t, exp, out)
		return nil
	}

	do := func(opts ...PoolOpt) {
		opts = append(opts, PoolOnFullClose())
		size := 10
		pool := testPool(size, opts...)
		var wg sync.WaitGroup
		for i := 0; i < size*4; i++ {
			wg.Add(1)
			go func() {
				for i := 0; i < 100; i++ {
					assert.NoError(t, pool.Do(WithConn("", testEcho)))
				}
				wg.Done()
			}()
		}
		wg.Wait()
		assert.Equal(t, size, pool.NumAvailConns())
		pool.Close()
		assert.Equal(t, 0, pool.NumAvailConns())
	}

	t.Run("onEmptyWait", func(t *T) { do(PoolOnEmptyWait()) })
	t.Run("onEmptyCreate", func(t *T) { do(PoolOnEmptyCreateAfter(0)) })
	t.Run("onEmptyCreateAfter", func(t *T) { do(PoolOnEmptyCreateAfter(1 * time.Second)) })
	// This one is expected to error, since this test empties the pool by design
	//t.Run("onEmptyErr", func(t *T) { do(PoolOnEmptyErrAfter(0)) })
	t.Run("onEmptyErrAfter", func(t *T) { do(PoolOnEmptyErrAfter(1 * time.Second)) })

	t.Run("withTrace", func(t *T) {
		var connCreatedCount int
		var connClosedCount int
		var doCompletedCount uint32
		pt := trace.PoolTrace{
			ConnCreated: func(done trace.PoolConnCreated) {
				connCreatedCount++
			},
			ConnClosed: func(closed trace.PoolConnClosed) {
				connClosedCount++
			},
			DoCompleted: func(completed trace.PoolDoCompleted) {
				atomic.AddUint32(&doCompletedCount, 1)
			},
		}
		do(PoolWithTrace(pt))
		if connCreatedCount != connClosedCount {
			t.Fail()
		}
		if doCompletedCount == 0 {
			t.Fail()
		}
	})
}

// Test all the different OnEmpty behaviors
func TestPoolGet(t *T) {
	getBlock := func(p *Pool) (time.Duration, error) {
		start := time.Now()
		_, err := p.get()
		return time.Since(start), err
	}

	// this one is a bit weird, cause it would block infinitely if we let it
	t.Run("onEmptyWait", func(t *T) {
		pool := testPool(1, PoolOnEmptyWait())
		conn, err := pool.get()
		assert.NoError(t, err)

		go func() {
			time.Sleep(2 * time.Second)
			pool.put(conn)
		}()
		took, err := getBlock(pool)
		assert.NoError(t, err)
		assert.True(t, took-2*time.Second < 20*time.Millisecond)
	})

	// the rest are pretty straightforward
	gen := func(mkOpt func(time.Duration) PoolOpt, d time.Duration, expErr error) func(*T) {
		return func(t *T) {
			pool := testPool(0, PoolOnFullClose(), mkOpt(d))
			took, err := getBlock(pool)
			assert.Equal(t, expErr, err)
			assert.True(t, took-d < 20*time.Millisecond)
		}
	}

	t.Run("onEmptyCreate", gen(PoolOnEmptyCreateAfter, 0, nil))
	t.Run("onEmptyCreateAfter", gen(PoolOnEmptyCreateAfter, 1*time.Second, nil))
	t.Run("onEmptyErr", gen(PoolOnEmptyErrAfter, 0, ErrPoolEmpty))
	t.Run("onEmptyErrAfter", gen(PoolOnEmptyErrAfter, 1*time.Second, ErrPoolEmpty))
}

func TestPoolOnFull(t *T) {
	t.Run("onFullClose", func(t *T) {
		pool := testPool(1, PoolOnFullClose())
		defer pool.Close()
		assert.Equal(t, 1, len(pool.pool))

		spc, err := pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 1, len(pool.pool))
	})

	t.Run("onFullBuffer", func(t *T) {
		pool := testPool(1, PoolOnFullBuffer(1, 1*time.Second))
		defer pool.Close()
		assert.Equal(t, 1, len(pool.pool))

		// putting a conn should overflow
		spc, err := pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 2, len(pool.pool))

		// another shouldn't, overflow is full
		spc, err = pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 2, len(pool.pool))

		// retrieve from the pool, drain shouldn't do anything because the
		// overflow is empty now
		<-pool.pool
		assert.Equal(t, 1, len(pool.pool))
		time.Sleep(2 * time.Second)
		assert.Equal(t, 1, len(pool.pool))

		// if both are full then drain should remove the overflow one
		spc, err = pool.newConn(false, "TEST")
		assert.NoError(t, err)
		pool.put(spc)
		assert.Equal(t, 2, len(pool.pool))
		time.Sleep(2 * time.Second)
		assert.Equal(t, 1, len(pool.pool))
	})
}

func TestPoolPut(t *T) {
	size := 10
	pool := testPool(size)

	assertPoolConns := func(exp int) {
		assert.Equal(t, exp, pool.NumAvailConns())
	}
	assertPoolConns(10)

	// Make sure that put does not accept a connection which has had a critical
	// network error
	pool.Do(WithConn("", func(conn Conn) error {
		assertPoolConns(9)
		conn.(*ioErrConn).lastIOErr = io.EOF
		return nil
	}))
	assertPoolConns(9)

	// Make sure that a put _does_ accept a connection which had a
	// marshal/unmarshal error
	pool.Do(WithConn("", func(conn Conn) error {
		assert.NotNil(t, conn.Do(FlatCmd(nil, "ECHO", "", func() {})))
		assert.Nil(t, conn.(*ioErrConn).lastIOErr)
		return nil
	}))
	assertPoolConns(9)

	// Make sure that a put _does_ accept a connection which had an app level
	// resp error
	pool.Do(WithConn("", func(conn Conn) error {
		assert.NotNil(t, Cmd(nil, "CMDDNE"))
		assert.Nil(t, conn.(*ioErrConn).lastIOErr)
		return nil
	}))
	assertPoolConns(9)

	// Make sure that closing the pool closes outstanding connections as well
	closeCh := make(chan bool)
	go func() {
		<-closeCh
		assert.Nil(t, pool.Close())
		closeCh <- true
	}()
	pool.Do(WithConn("", func(conn Conn) error {
		closeCh <- true
		<-closeCh
		return nil
	}))
	assertPoolConns(0)
}

func TestPoolClose(t *T) {
	pool := testPool(1)
	assert.NoError(t, pool.Do(Cmd(nil, "PING")))
	assert.NoError(t, pool.Close())
	assert.Error(t, errClientClosed, pool.Do(Cmd(nil, "PING")))
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/dimfeld/glog"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"testing"
	"time"
)

const testContent string = `# This is some content
And some more content

## It was the best of times.

### It was the worst of times.
Charles Dickens Charles Dickens`

var testHTML []string = []string{
	"
This is some content
",
	"
And some more content
",
	"
It was the best of times.
",
	"
It was the worst of times.
",
	"
Charles Dickens Charles Dickens
",
}

func init() {
	config = &Config{}
	glog.SetStderrThreshold("FATAL")
}

func testOnePost(t *testing.T, title, date, tags string, includePostHeaderLine bool,
	content string) {

	t.Logf("Testing %s,  %s, \"%s\", %s",
		title, date, tags, strconv.FormatBool(includePostHeaderLine))

	var expectedTime time.Time
	expectedTags := make(map[string]bool)
	var err error
	validPost := true

	f, err := ioutil.TempFile("", "testPost")
	if err != nil {
		t.Fatal("Creating test post:", err)
	}

	if title != "MISSING" {
		f.WriteString(title + "\n")
	} else {
		validPost = false
	}

	if date != "MISSING" {
		f.WriteString(date + "\n")
		expectedTime, err = time.Parse(PostTimeFormat, strings.TrimSpace(date))
		if err != nil {
			validPost = false
		}
	} else {
		validPost = false
	}

	if tags != "MISSING" {
		f.WriteString(tags + "\n")

		if tags != "" {
			tagsList := strings.Split(tags, ",")
			for _, tag := range tagsList {
				tag = strings.Title(strings.TrimSpace(tag))
				expectedTags[tag] = true
			}
		}
	} else {
		validPost = false
	}

	if includePostHeaderLine {
		f.WriteString("\n")
	} else {
		validPost = false
	}

	if content != "MISSING" {
		f.WriteString(content)
	} else {
		// Missing content is actually ok...
		content = ""
	}

	filename := f.Name()
	f.Close()

	defer os.Remove("filename")

	postWithContent, err := NewPost(filename, true)
	if validPost && err != nil {
		t.Error("Expected valid post but saw error", err)
		return
	} else if !validPost {
		if err == nil && postWithContent != nil {
			t.Error("Expected invalid post but parsing succeeded")
		}
		return
	}

	if postWithContent.SourcePath != filename {
		t.Errorf("Expected SourcePath %s, saw %s", filename, postWithContent.SourcePath)
	}

	expectedTitle := strings.TrimSpace(title)
	if postWithContent.Title != expectedTitle {
		t.Errorf("Expected title \"%s\", saw \"%s\"", expectedTitle, postWithContent.Title)
	}

	if !postWithContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Expected time \"%s\", saw \"%s\"", expectedTime, postWithContent.Timestamp)
	}

	for _, tag := range postWithContent.Tags {
		_, ok := expectedTags[tag]
		if !ok {
			t.Error("Unexpected tag", tag)
		}
		delete(expectedTags, tag)
	}

	for tag := range expectedTags {
		t.Error("Did not see expected tag", tag)
	}

	if string(postWithContent.Content) != content {
		t.Errorf("Expected content: %s\nSaw content: %s", content)
	}

	htmlContent := string(postWithContent.HTMLContent(false))

	if len(content) != 0 {
		for _, expectedHTML := range testHTML {
			if !strings.Contains(htmlContent, expectedHTML) {
				t.Errorf("Expected HTML to contain: %s\nSaw HTML: %s", testHTML, htmlContent)
			}
		}
	}

	postWithoutContent, err := NewPost(filename, false)
	if err != nil {
		t.Error("Error loading post without content:", err)
	}

	if postWithoutContent.SourcePath != filename {
		t.Errorf("Post without content expected SourcePath %s, saw %s",
			filename, postWithoutContent.SourcePath)
	}

	if postWithoutContent.Title != expectedTitle {
		t.Errorf("Post without content expected title %s, saw %s",
			expectedTitle, postWithoutContent.Title)
	}

	if !postWithoutContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Post without content expected time %s, saw %s",
			expectedTime, postWithoutContent.Timestamp)
	}

	for i := range postWithContent.Tags {
		if i >= len(postWithoutContent.Tags) {
			t.Errorf("Post without content is missing tag %d", i)
		} else if postWithContent.Tags[i] != postWithoutContent.Tags[i] {
			t.Errorf("Post without content expected tag #%d %s, has %s",
				i, postWithContent.Tags[i], postWithoutContent.Tags[i])
		}
	}

	if postWithoutContent.Content != nil {
		t.Error("Post without content had content", postWithoutContent.Content)
	}
}

func TestNewPost(t *testing.T) {
	titles := []string{"Valid Title", " Extra spaces ", "", "MISSING"}
	dates := []string{"10/12/14 4:15PM MST", "6/4/12 3:57AM CDT  ",
		"", "MISSING"}
	tags := []string{"onetag", "tag1,tag 2", "a long tag", "tag 1, tag2, tag 3", "", "MISSING"}
	contents := []string{testContent, "", "MISSING"}
	includePostHeaderLine := []bool{true, false}

	for _, title := range titles {
		for _, date := range dates {
			for _, tag := range tags {
				for _, include := range includePostHeaderLine {
					for _, content := range contents {
						testOnePost(t, title, date, tag, include, content)
						// if t.Failed() {
						// 	// Quit early if we fail to ease debugging.
						// 	t.FailNow()
						// }
					}
				}
			}
		}
	}

	f, err := ioutil.TempFile("", "temppost")
	if err != nil {
		t.Fatal("Failed to create temp file")
	}
	filename := f.Name()
	// Make the file unreadable.
	f.Chmod(0200)
	f.Close()
	defer os.Remove(filename)

	_, err = NewPost(filename, true)
	if err == nil {
		t.Error("No error returned when reading unreadable post")
	}

}

var testPosts []*Post

func createTestPosts(t *testing.T) {
	// Can't call time.Parse in a static initializer, so we make this here.
	testPosts = make([]*Post, 3)

	postTime, err := time.Parse(PostTimeFormat, "1/2/14 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[0] = &Post{"2014/02/test-post1.md",
		"TestPost1",
		postTime,
		[]string{"tag1", "tag2"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/3/14 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[1] = &Post{"2014/02/test-post2.md",
		"TestPost2",
		postTime,
		[]string{"tag1"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/2/12 4:15PM MST")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[2] = &Post{"2012/02/test-post3.md",
		"TestPost3",
		postTime,
		[]string{"tag2"},
		[]byte("content")}
}

func writePost(t *testing.T, postPath string, post *Post) {
	dir, _ := path.Split(post.SourcePath)
	dir = path.Join(postPath, dir)
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		t.Fatalf("Error making directory %s: %s", dir, err)
	}

	fullPath := path.Join(postPath, post.SourcePath)
	f, err := os.Create(fullPath)
	if err != nil {
		t.Fatalf("Error creating %s: %s", fullPath, err)
	}
	defer f.Close()

	f.WriteString(post.Title + "\n")
	postTime := post.Timestamp.Format("1/2/06 3:04PM MST")
	f.WriteString(postTime + "\n")
	f.WriteString(strings.Join(post.Tags, ",") + "\n\n")
	f.Write(post.Content)
}

func createPostTree(t *testing.T) (dirPath string) {
	createTestPosts(t)
	dirPath, err := ioutil.TempDir("", "testposts")
	if err != nil {
		t.Fatal("Could not create temporary directory:", err)
	}

	for _, post := range testPosts {
		writePost(t, dirPath, post)
	}

	return dirPath
}

func TestLoadPostsFromPath(t *testing.T) {
	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	checkPostList := func(postList PostList, expected []string) {
		if len(postList) != len(expected) {
			t.Errorf("Expected %d posts but found %d",
				len(expected), len(postList))
		}

		for i, exp := range expected {
			if postList[i].SourcePath != exp {
				t.Errorf("Expected post 0 to be %s, saw %s",
					exp, postList[i].SourcePath)
			}
		}

	}

	t.Log("Loading 3 valid posts")
	expectedSorted := []string{"2012/02/test-post3.md",
		"2014/02/test-post1.md",
		"2014/02/test-post2.md"}
	for i, val := range expectedSorted {
		expectedSorted[i] = path.Join(dir, val)
	}

	postList, err := LoadPostsFromPath(dir, true)
	sort.Sort(postList)

	checkPostList(postList, expectedSorted)

	t.Log("Test with a non-.md post")
	nonMdPost := &Post{"2012/02/other-post.txt",
		"TestPost4",
		time.Now(),
		[]string{"tag2"},
		[]byte("content")}
	writePost(t, dir, nonMdPost)
	nonMdPost.SourcePath = "2012/02/.somepost.md"
	writePost(t, dir, nonMdPost)

	postList, err = LoadPostsFromPath(dir, true)
	sort.Sort(postList)
	checkPostList(postList, expectedSorted)

	t.Log("Testing with 3 valid posts and 1 invalid post")
	ioutil.WriteFile(path.Join(dir, "2012/02/invalidpost.md"), []byte("Invalid post"), 0666)
	postList, err = LoadPostsFromPath(dir, true)
	if err == nil {
		t.Error("LoadPostsFromPath did not propagate error from NewPost")
	}
	if postList == nil || len(postList) != 3 {
		t.Error("LoadPostsFromPath did not load valid posts because of one invalid post.")
	}
	sort.Sort(postList)
	checkPostList(postList, expectedSorted)

	t.Log("Testing load from invalid path")
	_, err = LoadPostsFromPath("/jklsdfjklfds hjlksdfj", true)
	if err == nil {
		t.Error("No error returned on loading from nonexistent path.")
	}
}

func TestArchiveSpecList(t *testing.T) {
	checkSpecList := func(expected, actual ArchiveSpecList) {
		if len(expected) != len(actual) {
			t.Errorf("Expected %d specs but saw %d",
				len(expected), len(actual))
			return
		}

		for i, spec := range expected {
			if !time.Time(spec).Equal(time.Time(actual[i])) {
				t.Errorf("Expected spec #%d %s, saw %s",
					i, spec, actual[i])
			}
		}
	}

	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	expectedSpecList := ArchiveSpecList{
		ArchiveSpec(time.Date(2012, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
		ArchiveSpec(time.Date(2014, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
	}

	t.Log("Test normal spec list creation")
	specList, err := NewArchiveSpecList(dir)
	if err != nil {
		t.Error("Error creating spec list")
	}
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	href := specList[1].Href()
	if href != "/2014/02" {
		t.Errorf("Expected href %s, saw %s", "/2014/02", href)
	}

	text := specList[1].String()
	if text != "Feb 2014" {
		t.Errorf("Expected text %s, saw %s", "Feb 2014", "text")
	}

	t.Log("Test with some non-numeric directories")
	os.Mkdir(path.Join(dir, "abc"), 0755)
	os.Mkdir(path.Join(dir, "2012", "abc"), 0755)
	specList, err = NewArchiveSpecList(dir)
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	t.Log("Test reverse sort")
	expectedSpecList[0], expectedSpecList[1] = expectedSpecList[1], expectedSpecList[0]
	sort.Sort(sort.Reverse(specList))
	checkSpecList(expectedSpecList, specList)

	_, err = NewArchiveSpecList("/jklsdfnkjlse kslef")
	if err == nil {
		t.Error("NewArchiveSpecList did not fail with invalid directory")
	}
}
<commit_msg>Update tests to use new timezone format<commit_after>package main

import (
	"github.com/dimfeld/glog"
	"io/ioutil"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"testing"
	"time"
)

const testContent string = `# This is some content
And some more content

## It was the best of times.

### It was the worst of times.
Charles Dickens Charles Dickens`

var testHTML []string = []string{
	"
This is some content
",
	"
And some more content
",
	"
It was the best of times.
",
	"
It was the worst of times.
",
	"
Charles Dickens Charles Dickens
",
}

func init() {
	config = &Config{}
	glog.SetStderrThreshold("FATAL")
}

func testOnePost(t *testing.T, title, date, tags string, includePostHeaderLine bool,
	content string) {

	t.Logf("Testing %s,  %s, \"%s\", %s",
		title, date, tags, strconv.FormatBool(includePostHeaderLine))

	var expectedTime time.Time
	expectedTags := make(map[string]bool)
	var err error
	validPost := true

	f, err := ioutil.TempFile("", "testPost")
	if err != nil {
		t.Fatal("Creating test post:", err)
	}

	if title != "MISSING" {
		f.WriteString(title + "\n")
	} else {
		validPost = false
	}

	if date != "MISSING" {
		f.WriteString(date + "\n")
		expectedTime, err = time.Parse(PostTimeFormat, strings.TrimSpace(date))
		if err != nil {
			validPost = false
		}
	} else {
		validPost = false
	}

	if tags != "MISSING" {
		f.WriteString(tags + "\n")

		if tags != "" {
			tagsList := strings.Split(tags, ",")
			for _, tag := range tagsList {
				tag = strings.Title(strings.TrimSpace(tag))
				expectedTags[tag] = true
			}
		}
	} else {
		validPost = false
	}

	if includePostHeaderLine {
		f.WriteString("\n")
	} else {
		validPost = false
	}

	if content != "MISSING" {
		f.WriteString(content)
	} else {
		// Missing content is actually ok...
		content = ""
	}

	filename := f.Name()
	f.Close()

	defer os.Remove("filename")

	postWithContent, err := NewPost(filename, true)
	if validPost && err != nil {
		t.Error("Expected valid post but saw error", err)
		return
	} else if !validPost {
		if err == nil && postWithContent != nil {
			t.Error("Expected invalid post but parsing succeeded")
		}
		return
	}

	if postWithContent.SourcePath != filename {
		t.Errorf("Expected SourcePath %s, saw %s", filename, postWithContent.SourcePath)
	}

	expectedTitle := strings.TrimSpace(title)
	if postWithContent.Title != expectedTitle {
		t.Errorf("Expected title \"%s\", saw \"%s\"", expectedTitle, postWithContent.Title)
	}

	if !postWithContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Expected time \"%s\", saw \"%s\"", expectedTime, postWithContent.Timestamp)
	}

	for _, tag := range postWithContent.Tags {
		_, ok := expectedTags[tag]
		if !ok {
			t.Error("Unexpected tag", tag)
		}
		delete(expectedTags, tag)
	}

	for tag := range expectedTags {
		t.Error("Did not see expected tag", tag)
	}

	if string(postWithContent.Content) != content {
		t.Errorf("Expected content: %s\nSaw content: %s", content)
	}

	htmlContent := string(postWithContent.HTMLContent(false))

	if len(content) != 0 {
		for _, expectedHTML := range testHTML {
			if !strings.Contains(htmlContent, expectedHTML) {
				t.Errorf("Expected HTML to contain: %s\nSaw HTML: %s", testHTML, htmlContent)
			}
		}
	}

	postWithoutContent, err := NewPost(filename, false)
	if err != nil {
		t.Error("Error loading post without content:", err)
	}

	if postWithoutContent.SourcePath != filename {
		t.Errorf("Post without content expected SourcePath %s, saw %s",
			filename, postWithoutContent.SourcePath)
	}

	if postWithoutContent.Title != expectedTitle {
		t.Errorf("Post without content expected title %s, saw %s",
			expectedTitle, postWithoutContent.Title)
	}

	if !postWithoutContent.Timestamp.Equal(expectedTime) {
		t.Errorf("Post without content expected time %s, saw %s",
			expectedTime, postWithoutContent.Timestamp)
	}

	for i := range postWithContent.Tags {
		if i >= len(postWithoutContent.Tags) {
			t.Errorf("Post without content is missing tag %d", i)
		} else if postWithContent.Tags[i] != postWithoutContent.Tags[i] {
			t.Errorf("Post without content expected tag #%d %s, has %s",
				i, postWithContent.Tags[i], postWithoutContent.Tags[i])
		}
	}

	if postWithoutContent.Content != nil {
		t.Error("Post without content had content", postWithoutContent.Content)
	}
}

func TestNewPost(t *testing.T) {
	titles := []string{"Valid Title", " Extra spaces ", "", "MISSING"}
	dates := []string{"10/12/14 4:15PM -0700", "6/4/12 3:57AM -0500  ",
		"", "MISSING"}
	tags := []string{"onetag", "tag1,tag 2", "a long tag", "tag 1, tag2, tag 3", "", "MISSING"}
	contents := []string{testContent, "", "MISSING"}
	includePostHeaderLine := []bool{true, false}

	for _, title := range titles {
		for _, date := range dates {
			for _, tag := range tags {
				for _, include := range includePostHeaderLine {
					for _, content := range contents {
						testOnePost(t, title, date, tag, include, content)
						// if t.Failed() {
						// 	// Quit early if we fail to ease debugging.
						// 	t.FailNow()
						// }
					}
				}
			}
		}
	}

	f, err := ioutil.TempFile("", "temppost")
	if err != nil {
		t.Fatal("Failed to create temp file")
	}
	filename := f.Name()
	// Make the file unreadable.
	f.Chmod(0200)
	f.Close()
	defer os.Remove(filename)

	_, err = NewPost(filename, true)
	if err == nil {
		t.Error("No error returned when reading unreadable post")
	}

}

var testPosts []*Post

func createTestPosts(t *testing.T) {
	// Can't call time.Parse in a static initializer, so we make this here.
	testPosts = make([]*Post, 3)

	postTime, err := time.Parse(PostTimeFormat, "1/2/14 4:15PM -0700")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[0] = &Post{"2014/02/test-post1.md",
		"TestPost1",
		postTime,
		[]string{"tag1", "tag2"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/3/14 4:15PM -0700")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[1] = &Post{"2014/02/test-post2.md",
		"TestPost2",
		postTime,
		[]string{"tag1"},
		[]byte("content")}

	postTime, err = time.Parse(PostTimeFormat, "1/2/12 4:15PM -0700")
	if err != nil {
		t.Fatal("Invalid time in createTestPosts #0")
	}
	testPosts[2] = &Post{"2012/02/test-post3.md",
		"TestPost3",
		postTime,
		[]string{"tag2"},
		[]byte("content")}
}

func writePost(t *testing.T, postPath string, post *Post) {
	dir, _ := path.Split(post.SourcePath)
	dir = path.Join(postPath, dir)
	err := os.MkdirAll(dir, 0755)
	if err != nil {
		t.Fatalf("Error making directory %s: %s", dir, err)
	}

	fullPath := path.Join(postPath, post.SourcePath)
	f, err := os.Create(fullPath)
	if err != nil {
		t.Fatalf("Error creating %s: %s", fullPath, err)
	}
	defer f.Close()

	f.WriteString(post.Title + "\n")
	postTime := post.Timestamp.Format("1/2/06 3:04PM -0700")
	f.WriteString(postTime + "\n")
	f.WriteString(strings.Join(post.Tags, ",") + "\n\n")
	f.Write(post.Content)
}

func createPostTree(t *testing.T) (dirPath string) {
	createTestPosts(t)
	dirPath, err := ioutil.TempDir("", "testposts")
	if err != nil {
		t.Fatal("Could not create temporary directory:", err)
	}

	for _, post := range testPosts {
		writePost(t, dirPath, post)
	}

	return dirPath
}

func TestLoadPostsFromPath(t *testing.T) {
	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	checkPostList := func(postList PostList, expected []string) {
		if len(postList) != len(expected) {
			t.Errorf("Expected %d posts but found %d",
				len(expected), len(postList))
		}

		for i, exp := range expected {
			if postList[i].SourcePath != exp {
				t.Errorf("Expected post 0 to be %s, saw %s",
					exp, postList[i].SourcePath)
			}
		}

	}

	t.Log("Loading 3 valid posts")
	expectedSorted := []string{"2012/02/test-post3.md",
		"2014/02/test-post1.md",
		"2014/02/test-post2.md"}
	for i, val := range expectedSorted {
		expectedSorted[i] = path.Join(dir, val)
	}

	postList, err := LoadPostsFromPath(dir, true)
	sort.Sort(postList)

	checkPostList(postList, expectedSorted)

	t.Log("Test with a non-.md post")
	nonMdPost := &Post{"2012/02/other-post.txt",
		"TestPost4",
		time.Now(),
		[]string{"tag2"},
		[]byte("content")}
	writePost(t, dir, nonMdPost)
	nonMdPost.SourcePath = "2012/02/.somepost.md"
	writePost(t, dir, nonMdPost)

	postList, err = LoadPostsFromPath(dir, true)
	sort.Sort(postList)
	checkPostList(postList, expectedSorted)

	t.Log("Testing with 3 valid posts and 1 invalid post")
	ioutil.WriteFile(path.Join(dir, "2012/02/invalidpost.md"), []byte("Invalid post"), 0666)
	postList, err = LoadPostsFromPath(dir, true)
	if err == nil {
		t.Error("LoadPostsFromPath did not propagate error from NewPost")
	}
	if postList == nil || len(postList) != 3 {
		t.Error("LoadPostsFromPath did not load valid posts because of one invalid post.")
	}
	sort.Sort(postList)
	checkPostList(postList, expectedSorted)

	t.Log("Testing load from invalid path")
	_, err = LoadPostsFromPath("/jklsdfjklfds hjlksdfj", true)
	if err == nil {
		t.Error("No error returned on loading from nonexistent path.")
	}
}

func TestArchiveSpecList(t *testing.T) {
	checkSpecList := func(expected, actual ArchiveSpecList) {
		if len(expected) != len(actual) {
			t.Errorf("Expected %d specs but saw %d",
				len(expected), len(actual))
			return
		}

		for i, spec := range expected {
			if !time.Time(spec).Equal(time.Time(actual[i])) {
				t.Errorf("Expected spec #%d %s, saw %s",
					i, spec, actual[i])
			}
		}
	}

	dir := createPostTree(t)
	defer os.RemoveAll(dir)

	expectedSpecList := ArchiveSpecList{
		ArchiveSpec(time.Date(2012, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
		ArchiveSpec(time.Date(2014, time.Month(02), 1, 1, 1, 1, 1, time.UTC)),
	}

	t.Log("Test normal spec list creation")
	specList, err := NewArchiveSpecList(dir)
	if err != nil {
		t.Error("Error creating spec list")
	}
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	href := specList[1].Href()
	if href != "/2014/02" {
		t.Errorf("Expected href %s, saw %s", "/2014/02", href)
	}

	text := specList[1].String()
	if text != "Feb 2014" {
		t.Errorf("Expected text %s, saw %s", "Feb 2014", "text")
	}

	t.Log("Test with some non-numeric directories")
	os.Mkdir(path.Join(dir, "abc"), 0755)
	os.Mkdir(path.Join(dir, "2012", "abc"), 0755)
	specList, err = NewArchiveSpecList(dir)
	sort.Sort(specList)
	checkSpecList(expectedSpecList, specList)

	t.Log("Test reverse sort")
	expectedSpecList[0], expectedSpecList[1] = expectedSpecList[1], expectedSpecList[0]
	sort.Sort(sort.Reverse(specList))
	checkSpecList(expectedSpecList, specList)

	_, err = NewArchiveSpecList("/jklsdfnkjlse kslef")
	if err == nil {
		t.Error("NewArchiveSpecList did not fail with invalid directory")
	}
}
<|endoftext|>