<commit_before>package main

import (
	"context"
	"errors"
	"log"
	"testing"
	"time"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type testChecker struct {
	machines []*pbs.Config
	running  bool
}

func (t testChecker) assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config) {
	log.Printf("ASSESS %v", server)
	if server == "server1" {
		return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[0]
	}
	return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[1]
}

func (t testChecker) discover() *pbd.ServiceList {
	return &pbd.ServiceList{Services: []*pbd.RegistryEntry{&pbd.RegistryEntry{Identifier: "server1", Name: "gobuildslave"}, &pbd.RegistryEntry{Identifier: "server2", Name: "gobuildslave"}}}
}

func (t testChecker) getprev() []string {
	return make([]string, 0)
}

func (t testChecker) setprev(v []string) {
	// Do nothing
}

func (t testChecker) master(entry *pbd.RegistryEntry, master bool) (bool, error) {
	// Do nothing
	return true, nil
}

func TestPullData(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}})
	if val, ok := status["server1"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back bad: %v", status)
	}
}

func TestFleetCount(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}, running: false})
	if val, ok := status["server2"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back good when not running: %v", val)
	}
}

func TestLoadMainConfig(t *testing.T) {
	c, err := loadConfig()
	if err != nil {
		t.Errorf("Config load failed: %v", err)
	}
}

func TestRunJob(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 2}
	jobs := runJobs(&pb.Config{Intents: []*pb.Intent{i1}})
	log.Printf("RUN ON: %v", jobs)
	if len(jobs) != 2 || jobs[0].Name != "testing" || jobs[1].Name != "testing" {
		t.Errorf("Run jobs produced bad result: %v", jobs)
	}
}

func TestDiff(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 1 || diff.Intents[0].Spec.Name != "testing" {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestDiffWhenMatch(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{i1}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 0 && diff.Intents[0].Count != 0 {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestLoadOntoDiskMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 2000}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoAlreadyRunning(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{&pbs.Config{Disk: 100}, &pbs.Config{Disk: 1000}}})
	if server != "" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoExternalMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsexternal", External: true}

	machine1 := &pbs.Config{Disk: 100, External: false}
	machine2 := &pbs.Config{Disk: 100, External: true}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestDoubleLoadServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}
	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}
	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})

	if server == "server1" {
		t.Errorf("Loaded on server1 even though job was running there: %v", server)
	}
}

func TestMissServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "" {
		t.Errorf("Found a server even though one is not there: %v", server)
	}
}

type testGetter struct {
	failGetSlaves bool
	failGetJobs   bool
	running       map[string][]*pbs.JobAssignment
	config        map[string][]*pbs.Requirement
}

func (t *testGetter) getSlaves() (*pbd.ServiceList, error) {
	if t.failGetSlaves {
		return &pbd.ServiceList{}, errors.New("Built to fail")
	}

	list := &pbd.ServiceList{}
	for key := range t.running {
		list.Services = append(list.Services, &pbd.RegistryEntry{Identifier: key})
	}
	return list, nil
}

func (t *testGetter) getJobs(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.JobAssignment, error) {
	if t.failGetJobs {
		return []*pbs.JobAssignment{}, errors.New("Built to fail")
	}

	if val, ok := t.running[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.JobAssignment, 0), nil
}

func (t *testGetter) getConfig(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.Requirement, error) {
	if val, ok := t.config[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.Requirement, 0), nil
}

func TestFirstSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestDiskSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectWithLimits(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["badserver"] = []*pbs.JobAssignment{}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "backup"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectExternal(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["discover"] = []*pbs.JobAssignment{}
	tg.config["discover"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}}, tg)
	if server != "discover" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectFail(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisker"}, &pbs.Requirement{Category: pbs.RequirementCategory_ACCESS_POINT, Properties: "70:3A:CB:17:CF:BB"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestAddAccessPoints(t *testing.T) {
	s := InitTestServer()
	s.accessPoints["blah"] = time.Now().Add(-time.Hour * 25)
	for _, str := range []string{"70:3A:CB:17:CF:BB", "70:3A:CB:17:CC:D3", "70:3A:CB:17:CE:E3", "70:3A:CB:17:CF:BF", "blah", "70:3A:CB:17:CC:CF"} {
		s.addAccessPoint(context.Background(), str)
	}
}
<commit_msg>Tests pass<commit_after>package main

import (
	"context"
	"errors"
	"log"
	"testing"
	"time"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
)

type testChecker struct {
	machines []*pbs.Config
	running  bool
}

func (t testChecker) assess(ctx context.Context, server string) (*pbs.JobList, *pbs.Config) {
	log.Printf("ASSESS %v", server)
	if server == "server1" {
		return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[0]
	}
	return &pbs.JobList{Details: []*pbs.JobDetails{&pbs.JobDetails{Spec: &pbs.JobSpec{Name: "test1"}}}}, t.machines[1]
}

func (t testChecker) discover() *pbd.ServiceList {
	return &pbd.ServiceList{Services: []*pbd.RegistryEntry{&pbd.RegistryEntry{Identifier: "server1", Name: "gobuildslave"}, &pbd.RegistryEntry{Identifier: "server2", Name: "gobuildslave"}}}
}

func (t testChecker) getprev() []string {
	return make([]string, 0)
}

func (t testChecker) setprev(v []string) {
	// Do nothing
}

func (t testChecker) master(entry *pbd.RegistryEntry, master bool) (bool, error) {
	// Do nothing
	return true, nil
}

func TestPullData(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}})
	if val, ok := status["server1"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back bad: %v", status)
	}
}

func TestFleetCount(t *testing.T) {
	status, _ := getFleetStatus(context.Background(), &testChecker{machines: []*pbs.Config{&pbs.Config{}, &pbs.Config{}}, running: false})
	if val, ok := status["server2"]; !ok || len(val.Details) != 1 {
		t.Errorf("Status has come back good when not running: %v", val)
	}
}

func TestLoadMainConfig(t *testing.T) {
	_, err := loadConfig()
	if err != nil {
		t.Errorf("Config load failed: %v", err)
	}
}

func TestRunJob(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 2}
	jobs := runJobs(&pb.Config{Intents: []*pb.Intent{i1}})
	log.Printf("RUN ON: %v", jobs)
	if len(jobs) != 2 || jobs[0].Name != "testing" || jobs[1].Name != "testing" {
		t.Errorf("Run jobs produced bad result: %v", jobs)
	}
}

func TestDiff(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 1 || diff.Intents[0].Spec.Name != "testing" {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestDiffWhenMatch(t *testing.T) {
	i1 := &pb.Intent{Spec: &pbs.JobSpec{Name: "testing"}, Count: 1}
	c1 := &pb.Config{Intents: []*pb.Intent{i1}}
	c2 := &pb.Config{Intents: []*pb.Intent{i1}}

	diff := configDiff(c1, c2)
	if len(diff.Intents) != 0 && diff.Intents[0].Count != 0 {
		t.Errorf("Error in diff: %v", diff)
	}
}

func TestLoadOntoDiskMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 2000}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoAlreadyRunning(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{&pbs.Config{Disk: 100}, &pbs.Config{Disk: 1000}}})
	if server != "" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestLoadOntoExternalMachine(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsexternal", External: true}

	machine1 := &pbs.Config{Disk: 100, External: false}
	machine2 := &pbs.Config{Disk: 100, External: true}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "server2" {
		t.Errorf("Failed to select correct server: %v", server)
	}
}

func TestDoubleLoadServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "test1"}
	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}
	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})

	if server == "server1" {
		t.Errorf("Loaded on server1 even though job was running there: %v", server)
	}
}

func TestMissServer(t *testing.T) {
	conf := &pbs.JobSpec{Name: "needsdisk", Disk: 1024}

	machine1 := &pbs.Config{Disk: 100}
	machine2 := &pbs.Config{Disk: 100}

	server := chooseServer(context.Background(), conf, testChecker{machines: []*pbs.Config{machine1, machine2}})
	if server != "" {
		t.Errorf("Found a server even though one is not there: %v", server)
	}
}

type testGetter struct {
	failGetSlaves bool
	failGetJobs   bool
	running       map[string][]*pbs.JobAssignment
	config        map[string][]*pbs.Requirement
}

func (t *testGetter) getSlaves() (*pbd.ServiceList, error) {
	if t.failGetSlaves {
		return &pbd.ServiceList{}, errors.New("Built to fail")
	}

	list := &pbd.ServiceList{}
	for key := range t.running {
		list.Services = append(list.Services, &pbd.RegistryEntry{Identifier: key})
	}
	return list, nil
}

func (t *testGetter) getJobs(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.JobAssignment, error) {
	if t.failGetJobs {
		return []*pbs.JobAssignment{}, errors.New("Built to fail")
	}

	if val, ok := t.running[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.JobAssignment, 0), nil
}

func (t *testGetter) getConfig(ctx context.Context, e *pbd.RegistryEntry) ([]*pbs.Requirement, error) {
	if val, ok := t.config[e.Identifier]; ok {
		return val, nil
	}
	return make([]*pbs.Requirement, 0), nil
}

func TestFirstSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestDiskSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment)}
	tg.running["badserver"] = []*pbs.JobAssignment{&pbs.JobAssignment{Job: &pbs.Job{Name: "runner"}}}
	tg.running["goodserver"] = []*pbs.JobAssignment{}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner"}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectWithLimits(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["badserver"] = []*pbs.JobAssignment{}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "backup"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelect(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "goodserver" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectExternal(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["discover"] = []*pbs.JobAssignment{}
	tg.config["discover"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_EXTERNAL, Properties: "external_ready"}}}, tg)
	if server != "discover" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestReqSelectFail(t *testing.T) {
	s := InitTestServer()
	tg := &testGetter{running: make(map[string][]*pbs.JobAssignment), config: make(map[string][]*pbs.Requirement)}
	tg.running["goodserver"] = []*pbs.JobAssignment{}
	tg.config["goodserver"] = []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisker"}, &pbs.Requirement{Category: pbs.RequirementCategory_ACCESS_POINT, Properties: "70:3A:CB:17:CF:BB"}}

	server := s.selectServer(context.Background(), &pbs.Job{Name: "runner", Requirements: []*pbs.Requirement{&pbs.Requirement{Category: pbs.RequirementCategory_DISK, Properties: "maindisk"}}}, tg)
	if server != "" {
		t.Errorf("Wrong server selected: %v", server)
	}
}

func TestAddAccessPoints(t *testing.T) {
	s := InitTestServer()
	s.accessPoints["blah"] = time.Now().Add(-time.Hour * 25)
	for _, str := range []string{"70:3A:CB:17:CF:BB", "70:3A:CB:17:CC:D3", "70:3A:CB:17:CE:E3", "70:3A:CB:17:CF:BF", "blah", "70:3A:CB:17:CC:CF"} {
		s.addAccessPoint(context.Background(), str)
	}
}
<|endoftext|>


<commit_before>package md2man

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/russross/blackfriday/v2"
)

// roffRenderer implements the blackfriday.Renderer interface for creating
// roff format (manpages) from markdown text
type roffRenderer struct {
	extensions   blackfriday.Extensions
	listCounters []int
	firstHeader  bool
	defineTerm   bool
	listDepth    int
}

const (
	titleHeader      = ".TH "
	topLevelHeader   = "\n\n.SH "
	secondLevelHdr   = "\n.SH "
	otherHeader      = "\n.SS "
	crTag            = "\n"
	emphTag          = "\\fI"
	emphCloseTag     = "\\fP"
	strongTag        = "\\fB"
	strongCloseTag   = "\\fP"
	breakTag         = "\n.br\n"
	paraTag          = "\n.PP\n"
	hruleTag         = "\n.ti 0\n\\l'\\n(.lu'\n"
	linkTag          = "\n\\[la]"
	linkCloseTag     = "\\[ra]"
	codespanTag      = "\\fB\\fC"
	codespanCloseTag = "\\fR"
	codeTag          = "\n.PP\n.RS\n\n.nf\n"
	codeCloseTag     = "\n.fi\n.RE\n"
	quoteTag         = "\n.PP\n.RS\n"
	quoteCloseTag    = "\n.RE\n"
	listTag          = "\n.RS\n"
	listCloseTag     = "\n.RE\n"
	arglistTag       = "\n.TP\n"
	tableStart       = "\n.TS\nallbox;\n"
	tableEnd         = ".TE\n"
	tableCellStart   = "T{\n"
	tableCellEnd     = "\nT}\n"
)

// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
	var extensions blackfriday.Extensions

	extensions |= blackfriday.NoIntraEmphasis
	extensions |= blackfriday.Tables
	extensions |= blackfriday.FencedCode
	extensions |= blackfriday.SpaceHeadings
	extensions |= blackfriday.Footnotes
	extensions |= blackfriday.Titleblock
	extensions |= blackfriday.DefinitionLists
	return &roffRenderer{
		extensions: extensions,
	}
}

// GetExtensions returns the list of extensions used by this renderer implementation
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
	return r.extensions
}

// RenderHeader handles outputting the header at document start
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
	// disable hyphenation
	out(w, ".nh\n")
}

// RenderFooter handles outputting the footer at the document end; the roff
// renderer has no footer information
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
}

// RenderNode is called for each node in a markdown document; based on the node
// type the equivalent roff output is sent to the writer
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {

	var walkAction = blackfriday.GoToNext

	switch node.Type {
	case blackfriday.Text:
		r.handleText(w, node, entering)
	case blackfriday.Softbreak:
		out(w, crTag)
	case blackfriday.Hardbreak:
		out(w, breakTag)
	case blackfriday.Emph:
		if entering {
			out(w, emphTag)
		} else {
			out(w, emphCloseTag)
		}
	case blackfriday.Strong:
		if entering {
			out(w, strongTag)
		} else {
			out(w, strongCloseTag)
		}
	case blackfriday.Link:
		if !entering {
			out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
		}
	case blackfriday.Image:
		// ignore images
		walkAction = blackfriday.SkipChildren
	case blackfriday.Code:
		out(w, codespanTag)
		escapeSpecialChars(w, node.Literal)
		out(w, codespanCloseTag)
	case blackfriday.Document:
		break
	case blackfriday.Paragraph:
		// roff .PP markers break lists
		if r.listDepth > 0 {
			return blackfriday.GoToNext
		}
		if entering {
			out(w, paraTag)
		} else {
			out(w, crTag)
		}
	case blackfriday.BlockQuote:
		if entering {
			out(w, quoteTag)
		} else {
			out(w, quoteCloseTag)
		}
	case blackfriday.Heading:
		r.handleHeading(w, node, entering)
	case blackfriday.HorizontalRule:
		out(w, hruleTag)
	case blackfriday.List:
		r.handleList(w, node, entering)
	case blackfriday.Item:
		r.handleItem(w, node, entering)
	case blackfriday.CodeBlock:
		out(w, codeTag)
		escapeSpecialChars(w, node.Literal)
		out(w, codeCloseTag)
	case blackfriday.Table:
		r.handleTable(w, node, entering)
	case blackfriday.TableCell:
		r.handleTableCell(w, node, entering)
	case blackfriday.TableHead:
	case blackfriday.TableBody:
	case blackfriday.TableRow:
		// no action as cell entries do all the nroff formatting
		return blackfriday.GoToNext
	default:
		fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
	}
	return walkAction
}

func (r *roffRenderer) handleText(w io.Writer, node *blackfriday.Node, entering bool) {
	var (
		start, end string
	)
	// handle special roff table cell text encapsulation
	if node.Parent.Type == blackfriday.TableCell {
		if len(node.Literal) > 30 {
			start = tableCellStart
			end = tableCellEnd
		} else {
			// end rows that aren't terminated by "tableCellEnd" with a cr if end of row
			if node.Parent.Next == nil && !node.Parent.IsHeader {
				end = crTag
			}
		}
	}
	out(w, start)
	escapeSpecialChars(w, node.Literal)
	out(w, end)
}

func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
	if entering {
		switch node.Level {
		case 1:
			if !r.firstHeader {
				out(w, titleHeader)
				r.firstHeader = true
				break
			}
			out(w, topLevelHeader)
		case 2:
			out(w, secondLevelHdr)
		default:
			out(w, otherHeader)
		}
	}
}

func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
	openTag := listTag
	closeTag := listCloseTag
	if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
		// tags for definition lists handled within Item node
		openTag = ""
		closeTag = ""
	}
	if entering {
		r.listDepth++
		if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
			r.listCounters = append(r.listCounters, 1)
		}
		out(w, openTag)
	} else {
		if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
			r.listCounters = r.listCounters[:len(r.listCounters)-1]
		}
		out(w, closeTag)
		r.listDepth--
	}
}

func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
	if entering {
		if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
			out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
			r.listCounters[len(r.listCounters)-1]++
		} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
			// state machine for handling terms and following definitions
			// since blackfriday does not distinguish them properly, nor
			// does it seperate them into separate lists as it should
			if !r.defineTerm {
				out(w, arglistTag)
				r.defineTerm = true
			} else {
				r.defineTerm = false
			}
		} else {
			out(w, ".IP \\(bu 2\n")
		}
	} else {
		out(w, "\n")
	}
}

func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
	if entering {
		out(w, tableStart)
		//call walker to count cells (and rows?) so format section can be produced
		columns := countColumns(node)
		out(w, strings.Repeat("l ", columns)+"\n")
		out(w, strings.Repeat("l ", columns)+".\n")
	} else {
		out(w, tableEnd)
	}
}

func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
	var (
		start, end string
	)
	if node.IsHeader {
		start = codespanTag
		end = codespanCloseTag
	}
	if entering {
		if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
			out(w, "\t"+start)
		} else {
			out(w, start)
		}
	} else {
		// need to carriage return if we are at the end of the header row
		if node.IsHeader && node.Next == nil {
			end = end + crTag
		}
		out(w, end)
	}
}

// because roff format requires knowing the column count before outputting any table
// data we need to walk a table tree and count the columns
func countColumns(node *blackfriday.Node) int {
	var columns int

	node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
		switch node.Type {
		case blackfriday.TableRow:
			if !entering {
				return blackfriday.Terminate
			}
		case blackfriday.TableCell:
			if entering {
				columns++
			}
		default:
		}
		return blackfriday.GoToNext
	})
	return columns
}

func out(w io.Writer, output string) {
	io.WriteString(w, output) // nolint: errcheck
}

func needsBackslash(c byte) bool {
	for _, r := range []byte("-_&\\") {
		if c == r {
			return true
		}
	}
	return false
}

func escapeSpecialChars(w io.Writer, text []byte) {
	for i := 0; i < len(text); i++ {
		// escape initial apostrophe or period
		if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
			out(w, "\\&")
		}

		// directly copy normal characters
		org := i

		for i < len(text) && !needsBackslash(text[i]) {
			i++
		}
		if i > org {
			w.Write(text[org:i]) // nolint: errcheck
		}

		// escape a character
		if i >= len(text) {
			break
		}

		w.Write([]byte{'\\', text[i]}) // nolint: errcheck
	}
}
<commit_msg>added linebreak before .TE (table end)<commit_after>package md2man

import (
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/russross/blackfriday/v2"
)

// roffRenderer implements the blackfriday.Renderer interface for creating
// roff format (manpages) from markdown text
type roffRenderer struct {
	extensions   blackfriday.Extensions
	listCounters []int
	firstHeader  bool
	defineTerm   bool
	listDepth    int
}

const (
	titleHeader      = ".TH "
	topLevelHeader   = "\n\n.SH "
	secondLevelHdr   = "\n.SH "
	otherHeader      = "\n.SS "
	crTag            = "\n"
	emphTag          = "\\fI"
	emphCloseTag     = "\\fP"
	strongTag        = "\\fB"
	strongCloseTag   = "\\fP"
	breakTag         = "\n.br\n"
	paraTag          = "\n.PP\n"
	hruleTag         = "\n.ti 0\n\\l'\\n(.lu'\n"
	linkTag          = "\n\\[la]"
	linkCloseTag     = "\\[ra]"
	codespanTag      = "\\fB\\fC"
	codespanCloseTag = "\\fR"
	codeTag          = "\n.PP\n.RS\n\n.nf\n"
	codeCloseTag     = "\n.fi\n.RE\n"
	quoteTag         = "\n.PP\n.RS\n"
	quoteCloseTag    = "\n.RE\n"
	listTag          = "\n.RS\n"
	listCloseTag     = "\n.RE\n"
	arglistTag       = "\n.TP\n"
	tableStart       = "\n.TS\nallbox;\n"
	tableEnd         = "\n.TE\n"
	tableCellStart   = "T{\n"
	tableCellEnd     = "\nT}\n"
)

// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
	var extensions blackfriday.Extensions

	extensions |= blackfriday.NoIntraEmphasis
	extensions |= blackfriday.Tables
	extensions |= blackfriday.FencedCode
	extensions |= blackfriday.SpaceHeadings
	extensions |= blackfriday.Footnotes
	extensions |= blackfriday.Titleblock
	extensions |= blackfriday.DefinitionLists
	return &roffRenderer{
		extensions: extensions,
	}
}

// GetExtensions returns the list of extensions used by this renderer implementation
func (r *roffRenderer) GetExtensions() blackfriday.Extensions {
	return r.extensions
}

// RenderHeader handles outputting the header at document start
func (r *roffRenderer) RenderHeader(w io.Writer, ast *blackfriday.Node) {
	// disable hyphenation
	out(w, ".nh\n")
}

// RenderFooter handles outputting the footer at the document end; the roff
// renderer has no footer information
func (r *roffRenderer) RenderFooter(w io.Writer, ast *blackfriday.Node) {
}

// RenderNode is called for each node in a markdown document; based on the node
// type the equivalent roff output is sent to the writer
func (r *roffRenderer) RenderNode(w io.Writer, node *blackfriday.Node, entering bool) blackfriday.WalkStatus {

	var walkAction = blackfriday.GoToNext

	switch node.Type {
	case blackfriday.Text:
		r.handleText(w, node, entering)
	case blackfriday.Softbreak:
		out(w, crTag)
	case blackfriday.Hardbreak:
		out(w, breakTag)
	case blackfriday.Emph:
		if entering {
			out(w, emphTag)
		} else {
			out(w, emphCloseTag)
		}
	case blackfriday.Strong:
		if entering {
			out(w, strongTag)
		} else {
			out(w, strongCloseTag)
		}
	case blackfriday.Link:
		if !entering {
			out(w, linkTag+string(node.LinkData.Destination)+linkCloseTag)
		}
	case blackfriday.Image:
		// ignore images
		walkAction = blackfriday.SkipChildren
	case blackfriday.Code:
		out(w, codespanTag)
		escapeSpecialChars(w, node.Literal)
		out(w, codespanCloseTag)
	case blackfriday.Document:
		break
	case blackfriday.Paragraph:
		// roff .PP markers break lists
		if r.listDepth > 0 {
			return blackfriday.GoToNext
		}
		if entering {
			out(w, paraTag)
		} else {
			out(w, crTag)
		}
	case blackfriday.BlockQuote:
		if entering {
			out(w, quoteTag)
		} else {
			out(w, quoteCloseTag)
		}
	case blackfriday.Heading:
		r.handleHeading(w, node, entering)
	case blackfriday.HorizontalRule:
		out(w, hruleTag)
	case blackfriday.List:
		r.handleList(w, node, entering)
	case blackfriday.Item:
		r.handleItem(w, node, entering)
	case blackfriday.CodeBlock:
		out(w, codeTag)
		escapeSpecialChars(w, node.Literal)
		out(w, codeCloseTag)
	case blackfriday.Table:
		r.handleTable(w, node, entering)
	case blackfriday.TableCell:
		r.handleTableCell(w, node, entering)
	case blackfriday.TableHead:
	case blackfriday.TableBody:
	case blackfriday.TableRow:
		// no action as cell entries do all the nroff formatting
		return blackfriday.GoToNext
	default:
		fmt.Fprintln(os.Stderr, "WARNING: go-md2man does not handle node type "+node.Type.String())
	}
	return walkAction
}

func (r *roffRenderer) handleText(w io.Writer, node *blackfriday.Node, entering bool) {
	var (
		start, end string
	)
	// handle special roff table cell text encapsulation
	if node.Parent.Type == blackfriday.TableCell {
		if len(node.Literal) > 30 {
			start = tableCellStart
			end = tableCellEnd
		} else {
			// end rows that aren't terminated by "tableCellEnd" with a cr if end of row
			if node.Parent.Next == nil && !node.Parent.IsHeader {
				end = crTag
			}
		}
	}
	out(w, start)
	escapeSpecialChars(w, node.Literal)
	out(w, end)
}

func (r *roffRenderer) handleHeading(w io.Writer, node *blackfriday.Node, entering bool) {
	if entering {
		switch node.Level {
		case 1:
			if !r.firstHeader {
				out(w, titleHeader)
				r.firstHeader = true
				break
			}
			out(w, topLevelHeader)
		case 2:
			out(w, secondLevelHdr)
		default:
			out(w, otherHeader)
		}
	}
}

func (r *roffRenderer) handleList(w io.Writer, node *blackfriday.Node, entering bool) {
	openTag := listTag
	closeTag := listCloseTag
	if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
		// tags for definition lists handled within Item node
		openTag = ""
		closeTag = ""
	}
	if entering {
		r.listDepth++
		if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
			r.listCounters = append(r.listCounters, 1)
		}
		out(w, openTag)
	} else {
		if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
			r.listCounters = r.listCounters[:len(r.listCounters)-1]
		}
		out(w, closeTag)
		r.listDepth--
	}
}

func (r *roffRenderer) handleItem(w io.Writer, node *blackfriday.Node, entering bool) {
	if entering {
		if node.ListFlags&blackfriday.ListTypeOrdered != 0 {
			out(w, fmt.Sprintf(".IP \"%3d.\" 5\n", r.listCounters[len(r.listCounters)-1]))
			r.listCounters[len(r.listCounters)-1]++
		} else if node.ListFlags&blackfriday.ListTypeDefinition != 0 {
			// state machine for handling terms and following definitions
			// since blackfriday does not distinguish them properly, nor
			// does it seperate them into separate lists as it should
			if !r.defineTerm {
				out(w, arglistTag)
				r.defineTerm = true
			} else {
				r.defineTerm = false
			}
		} else {
			out(w, ".IP \\(bu 2\n")
		}
	} else {
		out(w, "\n")
	}
}

func (r *roffRenderer) handleTable(w io.Writer, node *blackfriday.Node, entering bool) {
	if entering {
		out(w, tableStart)
		//call walker to count cells (and rows?) so format section can be produced
		columns := countColumns(node)
		out(w, strings.Repeat("l ", columns)+"\n")
		out(w, strings.Repeat("l ", columns)+".\n")
	} else {
		out(w, tableEnd)
	}
}

func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, entering bool) {
	var (
		start, end string
	)
	if node.IsHeader {
		start = codespanTag
		end = codespanCloseTag
	}
	if entering {
		if node.Prev != nil && node.Prev.Type == blackfriday.TableCell {
			out(w, "\t"+start)
		} else {
			out(w, start)
		}
	} else {
		// need to carriage return if we are at the end of the header row
		if node.IsHeader && node.Next == nil {
			end = end + crTag
		}
		out(w, end)
	}
}

// because roff format requires knowing the column count before outputting any table
// data we need to walk a table tree and count the columns
func countColumns(node *blackfriday.Node) int {
	var columns int

	node.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
		switch node.Type {
		case blackfriday.TableRow:
			if !entering {
				return blackfriday.Terminate
			}
		case blackfriday.TableCell:
			if entering {
				columns++
			}
		default:
		}
		return blackfriday.GoToNext
	})
	return columns
}

func out(w io.Writer, output string) {
	io.WriteString(w, output) // nolint: errcheck
}

func needsBackslash(c byte) bool {
	for _, r := range []byte("-_&\\") {
		if c == r {
			return true
		}
	}
	return false
}

func escapeSpecialChars(w io.Writer, text []byte) {
	for i := 0; i < len(text); i++ {
		// escape initial apostrophe or period
		if len(text) >= 1 && (text[0] == '\'' || text[0] == '.') {
			out(w, "\\&")
		}

		// directly copy normal characters
		org := i

		for i < len(text) && !needsBackslash(text[i]) {
			i++
		}
		if i > org {
			w.Write(text[org:i]) // nolint: errcheck
		}

		// escape a character
		if i >= len(text) {
			break
		}

		w.Write([]byte{'\\', text[i]}) // nolint: errcheck
	}
}
<|endoftext|>


<commit_before>package mpris2

import (
	"fmt"
	"strings"
)

// http://specifications.freedesktop.org/mpris-spec/latest/

const (
	mprisPath          = "/org/mpris/MediaPlayer2"
	mprisPrefix       = "org.mpris.MediaPlayer2"
	rootInterface      = mprisPrefix
	playerInterface    = mprisPrefix + ".Player"
	trackListInterface = mprisPrefix + ".TrackList"
	playListsInterface = mprisPrefix + ".PlayLists"
)

type MediaPlayer struct {
	root   *Object
	player *Object
}

func (conn *Conn) GetMediaPlayer(objectName string) *MediaPlayer {
	if !strings.ContainsRune(objectName, '.') {
		objectName = fmt.Sprint(mprisPrefix, ".", objectName)
	}

	return &MediaPlayer{
		root:   conn.getObject(objectName, mprisPath, rootInterface),
		player: conn.getObject(objectName, mprisPath, playerInterface),
	}
}

func (conn *Conn) ListMediaPlayers() (names []string, err error) {
	allNames, err := conn.ListNames()
	if err != nil {
		return
	}

	for _, name := range allNames {
		if strings.HasPrefix(name, mprisPrefix) {
			names = append(names, name)
		}
	}
	return
}

func (conn *Conn) GetFirstMediaPlayer() (*MediaPlayer, error) {
	names, err := conn.ListMediaPlayers()
	if err != nil {
		return nil, err
	}

	if len(names) == 0 {
		return nil, fmt.Errorf("No media players found")
	}

	return conn.GetMediaPlayer(names[0]), nil
}

// root

func (mp *MediaPlayer) Raise() error {
	return mp.root.callVoid("Raise")
}

func (mp *MediaPlayer) Quit() error {
	return mp.root.callVoid("Quit")
}

func (mp *MediaPlayer) CanRaise() (bool, error) {
	return mp.root.getBool("CanRaise")
}

func (mp *MediaPlayer) CanQuit() (bool, error) {
	return mp.root.getBool("CanQuit")
}

func (mp *MediaPlayer) Identity() (string, error) {
	return mp.root.getString("Identity")
}

func (mp *MediaPlayer) DesktopEntry() (string, error) {
	return mp.root.getString("DesktopEntry")
}

func (mp *MediaPlayer) SupportedUriSchemes() ([]string, error) {
	return mp.root.getStringArray("SupportedUriSchemes")
}

func (mp *MediaPlayer) SupportedMimeTypes() ([]string, error) {
	return mp.root.getStringArray("SupportedMimeTypes")
}

// player

func (mp *MediaPlayer) Next() error {
	return mp.player.callVoid("Next")
}

func (mp *MediaPlayer) Previous() error {
	return mp.player.callVoid("Previous")
}

func (mp *MediaPlayer) Pause() error {
	return mp.player.callVoid("Pause")
}

func (mp *MediaPlayer) PlayPause() error {
	return mp.player.callVoid("PlayPause")
}

func (mp *MediaPlayer) Stop() error {
	return mp.player.callVoid("Stop")
}

func (mp *MediaPlayer) Play() error {
	return mp.player.callVoid("Play")
}

func (mp *MediaPlayer) Seek(offset int64) error {
	return mp.player.callVoid("Seek", offset)
}

func (mp *MediaPlayer) OpenUri(uri string) error {
	return mp.player.callVoid("OpenUri", uri)
}

func (mp *MediaPlayer) PlaybackStatus() (string, error) {
	return mp.player.getString("PlaybackStatus")
}

func (mp *MediaPlayer) Metadata() (Metadata, error) {
	return mp.player.getStringMap("Metadata")
}

func (mp *MediaPlayer) Position() (int64, error) {
	return mp.player.getInt64("Position")
}

func (mp *MediaPlayer) CanGoNext() (bool, error) {
	return mp.player.getBool("CanGoNext")
}

func (mp *MediaPlayer) CanGoPrevious() (bool, error) {
	return mp.player.getBool("CanGoPrevious")
}

func (mp *MediaPlayer) CanPlay() (bool, error) {
	return mp.player.getBool("CanPlay")
}

func (mp *MediaPlayer) CanPause() (bool, error) {
	return mp.player.getBool("CanPause")
}

func (mp *MediaPlayer) CanControl() (bool, error) {
	return mp.player.getBool("CanControl")
}
<commit_msg>Reformat repetative methods<commit_after>package mpris2

import (
	"fmt"
	"strings"
)

// http://specifications.freedesktop.org/mpris-spec/latest/

const (
	mprisPath          = "/org/mpris/MediaPlayer2"
	mprisPrefix        = "org.mpris.MediaPlayer2"
	rootInterface      = mprisPrefix
	playerInterface    = mprisPrefix + ".Player"
	trackListInterface = mprisPrefix + ".TrackList"
	playListsInterface = mprisPrefix + ".PlayLists"
)

type MediaPlayer struct {
	root   *Object
	player *Object
}

func (conn *Conn) GetMediaPlayer(objectName string) *MediaPlayer {
	if !strings.ContainsRune(objectName, '.') {
		objectName = fmt.Sprint(mprisPrefix, ".", objectName)
	}

	return &MediaPlayer{
		root:   conn.getObject(objectName, mprisPath, rootInterface),
		player: conn.getObject(objectName, mprisPath, playerInterface),
	}
}

func (conn *Conn) ListMediaPlayers() (names []string, err error) {
	allNames, err := conn.ListNames()
	if err != nil {
		return
	}

	for _, name := range allNames {
		if strings.HasPrefix(name, mprisPrefix) {
			names = append(names, name)
		}
	}
	return
}

func (conn *Conn) GetFirstMediaPlayer() (*MediaPlayer, error) {
	names, err := conn.ListMediaPlayers()
	if err != nil {
		return nil, err
	}

	if len(names) == 0 {
		return nil, fmt.Errorf("No media players found")
	}

	return conn.GetMediaPlayer(names[0]), nil
}

// root

func (mp *MediaPlayer) Raise() error                  { return mp.root.callVoid("Raise") }
func (mp *MediaPlayer) Quit() error                   { return mp.root.callVoid("Quit") }
func (mp *MediaPlayer) CanRaise() (bool, error)       { return mp.root.getBool("CanRaise") }
func (mp *MediaPlayer) CanQuit() (bool, error)        { return mp.root.getBool("CanQuit") }
func (mp *MediaPlayer) Identity() (string, error)     { return mp.root.getString("Identity") }
func (mp *MediaPlayer) DesktopEntry() (string, error) { return mp.root.getString("DesktopEntry") }

func (mp *MediaPlayer) SupportedUriSchemes() ([]string, error) {
	return mp.root.getStringArray("SupportedUriSchemes")
}

func (mp *MediaPlayer) SupportedMimeTypes() ([]string, error) {
	return mp.root.getStringArray("SupportedMimeTypes")
}

// player

func (mp *MediaPlayer) Next() error                     { return mp.player.callVoid("Next") }
func (mp *MediaPlayer) Previous() error                 { return mp.player.callVoid("Previous") }
func (mp *MediaPlayer) Pause() error                    { return mp.player.callVoid("Pause") }
func (mp *MediaPlayer) PlayPause() error                { return mp.player.callVoid("PlayPause") }
func (mp *MediaPlayer) Stop() error                     { return mp.player.callVoid("Stop") }
func (mp *MediaPlayer) Play() error                     { return mp.player.callVoid("Play") }
func (mp *MediaPlayer) Seek(offset int64) error         { return mp.player.callVoid("Seek", offset) }
func (mp *MediaPlayer) OpenUri(uri string) error        { return mp.player.callVoid("OpenUri", uri) }
func (mp *MediaPlayer) PlaybackStatus() (string, error) { return mp.player.getString("PlaybackStatus") }
func (mp *MediaPlayer) Metadata() (Metadata, error)     { return mp.player.getStringMap("Metadata") }
func (mp *MediaPlayer) Position() (int64, error)        { return mp.player.getInt64("Position") }
func (mp *MediaPlayer) CanGoNext() (bool, error)        { return mp.player.getBool("CanGoNext") }
func (mp *MediaPlayer) CanGoPrevious() (bool, error)    { return mp.player.getBool("CanGoPrevious") }
func (mp *MediaPlayer) CanPlay() (bool, error)          { return mp.player.getBool("CanPlay") }
func (mp *MediaPlayer) CanPause() (bool, error)         { return mp.player.getBool("CanPause") }
func (mp *MediaPlayer) CanControl() (bool, error)       { return mp.player.getBool("CanControl") }
<|endoftext|>


<commit_before>package main

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

func TestMemory(t *testing.T) {
	m := &Memory{}
	mh := &MetricHandler{}
	stats, _ := mh.Collect(m)

	assert.True(t, len(stats) > 10)
	assert.Equal(t, len(stats), 42)
	assert.Equal(t, stats[0].Key, "memory.MemTotal")
	assert.Equal(t, stats[0].Value, 502976)
}
<commit_msg>add helper to aggregate stats<commit_after>package main

import (
	"github.com/stretchr/testify/assert"
	"testing"
)

func aggregateStats(stats []*Metric) map[string]int64 {
	agg := map[string]int64{}
	for _, stat := range stats {
		agg[stat.Key] = stat.Value
	}
	return agg
}

func TestMemory(t *testing.T) {
	m := &Memory{}
	mh := &MetricHandler{}
	stats, _ := mh.Collect(m)

	assert.True(t, len(stats) > 10)
	assert.Equal(t, len(stats), 42)
	assert.Equal(t, stats[0].Key, "memory.MemTotal")
	assert.Equal(t, stats[0].Value, 502976)

	agg := aggregateStats(stats)
	assert.Equal(t, agg["memory.Committed_AS"], 350856)
}
<|endoftext|>


<commit_before>package mesh

import (
	"math"
	"sync"
)

type unicastRoutes map[PeerName]PeerName
type broadcastRoutes map[PeerName][]PeerName

type Routes struct {
	sync.RWMutex
	ourself      *LocalPeer
	peers        *Peers
	onChange     []func()
	unicast      unicastRoutes
	unicastAll   unicastRoutes // [1]
	broadcast    broadcastRoutes
	broadcastAll broadcastRoutes // [1]
	recalculate  chan<- *struct{}
	wait         chan<- chan struct{}
	// [1] based on *all* connections, not just established &
	// symmetric ones
}

func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
	recalculate := make(chan *struct{}, 1)
	wait := make(chan chan struct{})
	routes := &Routes{
		ourself:      ourself,
		peers:        peers,
		unicast:      make(unicastRoutes),
		unicastAll:   make(unicastRoutes),
		broadcast:    make(broadcastRoutes),
		broadcastAll: make(broadcastRoutes),
		recalculate:  recalculate,
		wait:         wait}
	routes.unicast[ourself.Name] = UnknownPeerName
	routes.unicastAll[ourself.Name] = UnknownPeerName
	routes.broadcast[ourself.Name] = []PeerName{}
	routes.broadcastAll[ourself.Name] = []PeerName{}
	go routes.run(recalculate, wait)
	return routes
}

func (routes *Routes) OnChange(callback func()) {
	routes.Lock()
	defer routes.Unlock()
	routes.onChange = append(routes.onChange, callback)
}

func (routes *Routes) PeerNames() PeerNameSet {
	return routes.peers.Names()
}

func (routes *Routes) Unicast(name PeerName) (PeerName, bool) {
	routes.RLock()
	defer routes.RUnlock()
	hop, found := routes.unicast[name]
	return hop, found
}

func (routes *Routes) UnicastAll(name PeerName) (PeerName, bool) {
	routes.RLock()
	defer routes.RUnlock()
	hop, found := routes.unicastAll[name]
	return hop, found
}

func (routes *Routes) Broadcast(name PeerName) []PeerName {
	routes.RLock()
	defer routes.RUnlock()
	return routes.broadcast[name]
}

func (routes *Routes) BroadcastAll(name PeerName) []PeerName {
	routes.RLock()
	defer routes.RUnlock()
	return routes.broadcastAll[name]
}

// Choose min(log2(n_peers), n_neighbouring_peers) neighbours, with a
// random distribution that is topology-sensitive, favouring
// neighbours at the end of "bottleneck links". We determine the
// latter based on the unicast routing table. If a neighbour appears
// as the value more frequently than others - meaning that we reach a
// higher proportion of peers via that neighbour than other neighbours
// - then it is chosen with a higher probability.
//
// Note that we choose log2(n_peers) *neighbours*, not
// peers. Consequently, on sparsely connected peers this function
// returns a higher proportion of neighbours than elsewhere. In
// extremis, on peers with fewer than log2(n_peers) neighbours, all
// neighbours are returned.
func (routes *Routes) RandomNeighbours(except PeerName) []PeerName {
	destinations := make(PeerNameSet)
	routes.RLock()
	defer routes.RUnlock()
	count := int(math.Log2(float64(len(routes.unicastAll))))
	// depends on go's random map iteration
	for _, dst := range routes.unicastAll {
		if dst != UnknownPeerName && dst != except {
			destinations[dst] = void
			if len(destinations) >= count {
				break
			}
		}
	}
	res := make([]PeerName, 0, len(destinations))
	for dst := range destinations {
		res = append(res, dst)
	}
	return res
}

// Request recalculation of the routing table. This is async but can
// effectively be made synchronous with a subsequent call to
// EnsureRecalculated.
func (routes *Routes) Recalculate() {
	// The use of a 1-capacity channel in combination with the
	// non-blocking send is an optimisation that results in multiple
	// requests being coalesced.
	select {
	case routes.recalculate <- nil:
	default:
	}
}

// Wait for any preceding Recalculate requests to be processed.
func (routes *Routes) EnsureRecalculated() {
	done := make(chan struct{})
	routes.wait <- done
	<-done
}

func (routes *Routes) run(recalculate <-chan *struct{}, wait <-chan chan struct{}) {
	for {
		select {
		case <-recalculate:
			routes.calculate()
		case done := <-wait:
			select {
			case <-recalculate:
				routes.calculate()
			default:
			}
			close(done)
		}
	}
}

func (routes *Routes) calculate() {
	routes.peers.RLock()
	routes.ourself.RLock()
	var (
		oldUnicast   = routes.unicast
		oldBroadcast = routes.broadcast
		unicast      = routes.calculateUnicast(true)
		unicastAll   = routes.calculateUnicast(false)
		broadcast    = routes.calculateBroadcast(true)
		broadcastAll = routes.calculateBroadcast(false)
	)
	routes.ourself.RUnlock()
	routes.peers.RUnlock()

	routes.Lock()
	routes.unicast = unicast
	routes.unicastAll = unicastAll
	routes.broadcast = broadcast
	routes.broadcastAll = broadcastAll
	onChange := routes.onChange
	routes.Unlock()

	if !unicast.equals(oldUnicast) || !broadcast.equals(oldBroadcast) {
		for _, callback := range onChange {
			callback()
		}
	}
}

// Calculate all the routes for the question: if *we* want to send a
// packet to Peer X, what is the next hop?
//
// When we sniff a packet, we determine the destination peer
// ourself. Consequently, we can relay the packet via any
// arbitrary peers - the intermediate peers do not have to have
// any knowledge of the MAC address at all. Thus there's no need
// to exchange knowledge of MAC addresses, nor any constraints on
// the routes that we construct.
func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) unicastRoutes {
	_, unicast := routes.ourself.Routes(nil, establishedAndSymmetric)
	return unicast
}

// Calculate all the routes for the question: if we receive a
// broadcast originally from Peer X, which peers should we pass the
// frames on to?
//
// When the topology is stable, and thus all peers perform route
// calculations based on the same data, the algorithm ensures that
// broadcasts reach every peer exactly once.
//
// This is largely due to properties of the Peer.Routes algorithm. In
// particular:
//
// ForAll X,Y,Z in Peers.
//     X.Routes(Y) <= X.Routes(Z) \/
//     X.Routes(Z) <= X.Routes(Y)
// ForAll X,Y,Z in Peers.
//     Y =/= Z /\ X.Routes(Y) <= X.Routes(Z) =>
//     X.Routes(Y) u [P | Y.HasSymmetricConnectionTo(P)] <= X.Routes(Z)
// where <= is the subset relationship on keys of the returned map.
func (routes *Routes) calculateBroadcast(establishedAndSymmetric bool) broadcastRoutes {
	broadcast := make(broadcastRoutes)
	for _, peer := range routes.peers.byName {
		hops := []PeerName{}
		if found, reached := peer.Routes(routes.ourself.Peer, establishedAndSymmetric); found {
			routes.ourself.ForEachConnectedPeer(establishedAndSymmetric, reached,
				func(remotePeer *Peer) { hops = append(hops, remotePeer.Name) })
		}
		broadcast[peer.Name] = hops
	}
	return broadcast
}

func (a unicastRoutes) equals(b unicastRoutes) bool {
	for key, aval := range a {
		if bval, ok := b[key]; !ok || bval != aval {
			return false
		}
	}

	for key := range b {
		if _, ok := a[key]; !ok {
			return false
		}
	}

	return true
}

func (a broadcastRoutes) equals(b broadcastRoutes) bool {
	set := make(map[PeerName]struct{})

	for key, aval := range a {
		bval, ok := b[key]
		if !ok {
			return false
		}

		for _, peer := range aval {
			set[peer] = struct{}{}
		}

		for _, peer := range bval {
			if _, ok := set[peer]; !ok {
				return false
			}

			delete(set, peer)
		}

		if len(set) != 0 {
			return false
		}

	}

	for key := range b {
		if _, ok := a[key]; !ok {
			return false
		}
	}

	return true
}
<commit_msg>lazy route calculation<commit_after>package mesh

import (
	"math"
	"sync"
)

type unicastRoutes map[PeerName]PeerName
type broadcastRoutes map[PeerName][]PeerName

type Routes struct {
	sync.RWMutex
	ourself      *LocalPeer
	peers        *Peers
	onChange     []func()
	unicast      unicastRoutes
	unicastAll   unicastRoutes // [1]
	broadcast    broadcastRoutes
	broadcastAll broadcastRoutes // [1]
	recalculate  chan<- *struct{}
	wait         chan<- chan struct{}
	action       chan<- func()
	// [1] based on *all* connections, not just established &
	// symmetric ones
}

func NewRoutes(ourself *LocalPeer, peers *Peers) *Routes {
	recalculate := make(chan *struct{}, 1)
	wait := make(chan chan struct{})
	action := make(chan func())
	routes := &Routes{
		ourself:      ourself,
		peers:        peers,
		unicast:      make(unicastRoutes),
		unicastAll:   make(unicastRoutes),
		broadcast:    make(broadcastRoutes),
		broadcastAll: make(broadcastRoutes),
		recalculate:  recalculate,
		wait:         wait,
		action:       action}
	routes.unicast[ourself.Name] = UnknownPeerName
	routes.unicastAll[ourself.Name] = UnknownPeerName
	routes.broadcast[ourself.Name] = []PeerName{}
	routes.broadcastAll[ourself.Name] = []PeerName{}
	go routes.run(recalculate, wait, action)
	return routes
}

func (routes *Routes) OnChange(callback func()) {
	routes.Lock()
	defer routes.Unlock()
	routes.onChange = append(routes.onChange, callback)
}

func (routes *Routes) PeerNames() PeerNameSet {
	return routes.peers.Names()
}

func (routes *Routes) Unicast(name PeerName) (PeerName, bool) {
	routes.RLock()
	defer routes.RUnlock()
	hop, found := routes.unicast[name]
	return hop, found
}

func (routes *Routes) UnicastAll(name PeerName) (PeerName, bool) {
	routes.RLock()
	defer routes.RUnlock()
	hop, found := routes.unicastAll[name]
	return hop, found
}

func (routes *Routes) Broadcast(name PeerName) []PeerName {
	routes.RLock()
	hops, found := routes.broadcast[name]
	routes.RUnlock()
	if found {
		return hops
	}
	res := make(chan []PeerName)
	routes.action <- func() {
		routes.RLock()
		hops, found := routes.broadcast[name]
		routes.RUnlock()
		if found {
			res <- hops
		}
		routes.peers.RLock()
		routes.ourself.RLock()
		hops = routes.calculateBroadcast(name, true)
		routes.ourself.RUnlock()
		routes.peers.RUnlock()
		res <- hops
		routes.Lock()
		routes.broadcast[name] = hops
		routes.Unlock()
	}
	return <-res
}

func (routes *Routes) BroadcastAll(name PeerName) []PeerName {
	routes.RLock()
	hops, found := routes.broadcastAll[name]
	routes.RUnlock()
	if found {
		return hops
	}
	res := make(chan []PeerName)
	routes.action <- func() {
		routes.RLock()
		hops, found := routes.broadcastAll[name]
		routes.RUnlock()
		if found {
			res <- hops
		}
		routes.peers.RLock()
		routes.ourself.RLock()
		hops = routes.calculateBroadcast(name, false)
		routes.ourself.RUnlock()
		routes.peers.RUnlock()
		res <- hops
		routes.Lock()
		routes.broadcastAll[name] = hops
		routes.Unlock()
	}
	return <-res
}

// Choose min(log2(n_peers), n_neighbouring_peers) neighbours, with a
// random distribution that is topology-sensitive, favouring
// neighbours at the end of "bottleneck links". We determine the
// latter based on the unicast routing table. If a neighbour appears
// as the value more frequently than others - meaning that we reach a
// higher proportion of peers via that neighbour than other neighbours
// - then it is chosen with a higher probability.
//
// Note that we choose log2(n_peers) *neighbours*, not
// peers. Consequently, on sparsely connected peers this function
// returns a higher proportion of neighbours than elsewhere. In
// extremis, on peers with fewer than log2(n_peers) neighbours, all
// neighbours are returned.
func (routes *Routes) RandomNeighbours(except PeerName) []PeerName {
	destinations := make(PeerNameSet)
	routes.RLock()
	defer routes.RUnlock()
	count := int(math.Log2(float64(len(routes.unicastAll))))
	// depends on go's random map iteration
	for _, dst := range routes.unicastAll {
		if dst != UnknownPeerName && dst != except {
			destinations[dst] = void
			if len(destinations) >= count {
				break
			}
		}
	}
	res := make([]PeerName, 0, len(destinations))
	for dst := range destinations {
		res = append(res, dst)
	}
	return res
}

// Request recalculation of the routing table. This is async but can
// effectively be made synchronous with a subsequent call to
// EnsureRecalculated.
func (routes *Routes) Recalculate() {
	// The use of a 1-capacity channel in combination with the
	// non-blocking send is an optimisation that results in multiple
	// requests being coalesced.
	select {
	case routes.recalculate <- nil:
	default:
		log.Println("route recalculation coalesced")
	}
}

// Wait for any preceding Recalculate requests to be processed.
func (routes *Routes) EnsureRecalculated() {
	done := make(chan struct{})
	routes.wait <- done
	<-done
}

func (routes *Routes) run(recalculate <-chan *struct{}, wait <-chan chan struct{}, action <-chan func()) {
	for {
		select {
		case <-recalculate:
			routes.calculate()
		case done := <-wait:
			select {
			case <-recalculate:
				routes.calculate()
			default:
			}
			close(done)
		case f := <-action:
			f()
		}
	}
}

func (routes *Routes) calculate() {
	routes.peers.RLock()
	routes.ourself.RLock()
	var (
		unicast      = routes.calculateUnicast(true)
		unicastAll   = routes.calculateUnicast(false)
		broadcast    = make(broadcastRoutes)
		broadcastAll = make(broadcastRoutes)
	)
	broadcast[routes.ourself.Name] = routes.calculateBroadcast(routes.ourself.Name, true)
	broadcastAll[routes.ourself.Name] = routes.calculateBroadcast(routes.ourself.Name, false)
	routes.ourself.RUnlock()
	routes.peers.RUnlock()

	routes.Lock()
	routes.unicast = unicast
	routes.unicastAll = unicastAll
	routes.broadcast = broadcast
	routes.broadcastAll = broadcastAll
	onChange := routes.onChange
	routes.Unlock()

	for _, callback := range onChange {
		callback()
	}
}

// Calculate all the routes for the question: if *we* want to send a
// packet to Peer X, what is the next hop?
//
// When we sniff a packet, we determine the destination peer
// ourself. Consequently, we can relay the packet via any
// arbitrary peers - the intermediate peers do not have to have
// any knowledge of the MAC address at all. Thus there's no need
// to exchange knowledge of MAC addresses, nor any constraints on
// the routes that we construct.
func (routes *Routes) calculateUnicast(establishedAndSymmetric bool) unicastRoutes {
	_, unicast := routes.ourself.Routes(nil, establishedAndSymmetric)
	return unicast
}

// Calculate the route to answer the question: if we receive a
// broadcast originally from Peer X, which peers should we pass the
// frames on to?
//
// When the topology is stable, and thus all peers perform route
// calculations based on the same data, the algorithm ensures that
// broadcasts reach every peer exactly once.
//
// This is largely due to properties of the Peer.Routes algorithm. In
// particular:
//
// ForAll X,Y,Z in Peers.
//     X.Routes(Y) <= X.Routes(Z) \/
//     X.Routes(Z) <= X.Routes(Y)
// ForAll X,Y,Z in Peers.
//     Y =/= Z /\ X.Routes(Y) <= X.Routes(Z) =>
//     X.Routes(Y) u [P | Y.HasSymmetricConnectionTo(P)] <= X.Routes(Z)
// where <= is the subset relationship on keys of the returned map.
func (routes *Routes) calculateBroadcast(name PeerName, establishedAndSymmetric bool) []PeerName {
	hops := []PeerName{}
	peer, found := routes.peers.byName[name]
	if !found {
		return hops
	}
	if found, reached := peer.Routes(routes.ourself.Peer, establishedAndSymmetric); found {
		routes.ourself.ForEachConnectedPeer(establishedAndSymmetric, reached,
			func(remotePeer *Peer) { hops = append(hops, remotePeer.Name) })
	}
	return hops
}
<|endoftext|>


<commit_before>package memdb

import (
	"reflect"
	"testing"
)

// Test that multiple concurrent transactions are isolated from each other
func TestTxn_Isolation(t *testing.T) {
	db := testDB(t)
	txn1 := db.Txn(true)

	obj := &TestObject{
		ID:  "my-object",
		Foo: "abc",
		Qux: []string{"abc1", "abc2"},
	}
	obj2 := &TestObject{
		ID:  "my-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}
	obj3 := &TestObject{
		ID:  "my-other-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}

	err := txn1.Insert("main", obj)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj3)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Results should show up in this transaction
	raw, err := txn1.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw == nil {
		t.Fatalf("bad: %#v", raw)
	}

	// Create a new transaction, current one is NOT committed
	txn2 := db.Txn(false)

	// Nothing should show up in this transaction
	raw, err = txn2.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw != nil {
		t.Fatalf("bad: %#v", raw)
	}

	// Commit txn1, txn2 should still be isolated
	txn1.Commit()

	// Nothing should show up in this transaction
	raw, err = txn2.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw != nil {
		t.Fatalf("bad: %#v", raw)
	}

	// Create a new txn
	txn3 := db.Txn(false)

	// Results should show up in this transaction
	raw, err = txn3.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw == nil {
		t.Fatalf("bad: %#v", raw)
	}
}

// Test that an abort clears progress
func TestTxn_Abort(t *testing.T) {
	db := testDB(t)
	txn1 := db.Txn(true)

	obj := &TestObject{
		ID:  "my-object",
		Foo: "abc",
		Qux: []string{"abc1", "abc2"},
	}
	obj2 := &TestObject{
		ID:  "my-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}
	obj3 := &TestObject{
		ID:  "my-other-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}

	err := txn1.Insert("main", obj)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj3)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Abort the txn
	txn1.Abort()
	txn1.Commit()

	// Create a new transaction
	txn2 := db.Txn(false)

	// Nothing should show up in this transaction
	raw, err := txn2.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw != nil {
		t.Fatalf("bad: %#v", raw)
	}
}

func TestComplexDB(t *testing.T) {
	db := testComplexDB(t)
	testPopulateData(t, db)
	txn := db.Txn(false) // read only

	// Get using a full name
	raw, err := txn.First("people", "name", "Armon", "Dadgar")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	// Get using a prefix
	raw, err = txn.First("people", "name_prefix", "Armon")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	raw, err = txn.First("people", "id_prefix", raw.(*TestPerson).ID[:4])
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	// Get based on field set.
	result, err := txn.Get("people", "sibling", true)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	exp := map[string]bool{"Alex": true, "Armon": true}
	act := make(map[string]bool, 2)
	for i := result.Next(); i != nil; i = result.Next() {
		p, ok := i.(*TestPerson)
		if !ok {
			t.Fatalf("should get person")
		}
		act[p.First] = true
	}

	if !reflect.DeepEqual(act, exp) {
		t.Fatalf("Got %#v; want %#v", act, exp)
	}

	raw, err = txn.First("people", "sibling", false)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}
	if raw.(*TestPerson).First != "Mitchell" {
		t.Fatalf("wrong person!")
	}

	// Where in the world is mitchell hashimoto?
	raw, err = txn.First("people", "name_prefix", "Mitchell")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	person := raw.(*TestPerson)
	if person.First != "Mitchell" {
		t.Fatalf("wrong person!")
	}

	raw, err = txn.First("visits", "id_prefix", person.ID)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get visit")
	}

	visit := raw.(*TestVisit)

	raw, err = txn.First("places", "id", visit.Place)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get place")
	}

	place := raw.(*TestPlace)
	if place.Name != "Maui" {
		t.Fatalf("bad place (but isn't anywhere else really?): %v", place)
	}
}

func testPopulateData(t *testing.T, db *MemDB) {
	// Start write txn
	txn := db.Txn(true)

	// Create some data
	person1 := testPerson()

	person2 := testPerson()
	person2.First = "Mitchell"
	person2.Last = "Hashimoto"

	person3 := testPerson()
	person3.First = "Alex"
	person3.Last = "Dadgar"

	person1.Sibling = person3
	person3.Sibling = person1

	place1 := testPlace()
	place2 := testPlace()
	place2.Name = "Maui"

	visit1 := &TestVisit{person1.ID, place1.ID}
	visit2 := &TestVisit{person2.ID, place2.ID}

	// Insert it all
	noErr(t, txn.Insert("people", person1))
	noErr(t, txn.Insert("people", person2))
	noErr(t, txn.Insert("people", person3))
	noErr(t, txn.Insert("places", place1))
	noErr(t, txn.Insert("places", place2))
	noErr(t, txn.Insert("visits", visit1))
	noErr(t, txn.Insert("visits", visit2))

	// Commit
	txn.Commit()
}

func noErr(t *testing.T, err error) {
	if err != nil {
		t.Fatalf("err: %v", err)
	}
}

type TestPerson struct {
	ID      string
	First   string
	Last    string
	Sibling *TestPerson
}

type TestPlace struct {
	ID   string
	Name string
}

type TestVisit struct {
	Person string
	Place  string
}

func testComplexSchema() *DBSchema {
	return &DBSchema{
		Tables: map[string]*TableSchema{
			"people": &TableSchema{
				Name: "people",
				Indexes: map[string]*IndexSchema{
					"id": &IndexSchema{
						Name:    "id",
						Unique:  true,
						Indexer: &UUIDFieldIndex{Field: "ID"},
					},
					"name": &IndexSchema{
						Name:   "name",
						Unique: true,
						Indexer: &CompoundIndex{
							Indexes: []Indexer{
								&StringFieldIndex{Field: "First"},
								&StringFieldIndex{Field: "Last"},
							},
						},
					},
					"sibling": &IndexSchema{
						Name:    "sibling",
						Unique:  false,
						Indexer: &FieldSetIndex{Field: "Sibling"},
					},
				},
			},
			"places": &TableSchema{
				Name: "places",
				Indexes: map[string]*IndexSchema{
					"id": &IndexSchema{
						Name:    "id",
						Unique:  true,
						Indexer: &UUIDFieldIndex{Field: "ID"},
					},
					"name": &IndexSchema{
						Name:    "name",
						Unique:  true,
						Indexer: &StringFieldIndex{Field: "Name"},
					},
				},
			},
			"visits": &TableSchema{
				Name: "visits",
				Indexes: map[string]*IndexSchema{
					"id": &IndexSchema{
						Name:   "id",
						Unique: true,
						Indexer: &CompoundIndex{
							Indexes: []Indexer{
								&UUIDFieldIndex{Field: "Person"},
								&UUIDFieldIndex{Field: "Place"},
							},
						},
					},
				},
			},
		},
	}
}

func testComplexDB(t *testing.T) *MemDB {
	db, err := NewMemDB(testComplexSchema())
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	return db
}

func testPerson() *TestPerson {
	_, uuid := generateUUID()
	obj := &TestPerson{
		ID:    uuid,
		First: "Armon",
		Last:  "Dadgar",
	}
	return obj
}

func testPlace() *TestPlace {
	_, uuid := generateUUID()
	obj := &TestPlace{
		ID:   uuid,
		Name: "HashiCorp",
	}
	return obj
}
<commit_msg>Adding basic test for watching<commit_after>package memdb

import (
	"reflect"
	"testing"
	"time"
)

// Test that multiple concurrent transactions are isolated from each other
func TestTxn_Isolation(t *testing.T) {
	db := testDB(t)
	txn1 := db.Txn(true)

	obj := &TestObject{
		ID:  "my-object",
		Foo: "abc",
		Qux: []string{"abc1", "abc2"},
	}
	obj2 := &TestObject{
		ID:  "my-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}
	obj3 := &TestObject{
		ID:  "my-other-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}

	err := txn1.Insert("main", obj)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj3)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Results should show up in this transaction
	raw, err := txn1.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw == nil {
		t.Fatalf("bad: %#v", raw)
	}

	// Create a new transaction, current one is NOT committed
	txn2 := db.Txn(false)

	// Nothing should show up in this transaction
	raw, err = txn2.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw != nil {
		t.Fatalf("bad: %#v", raw)
	}

	// Commit txn1, txn2 should still be isolated
	txn1.Commit()

	// Nothing should show up in this transaction
	raw, err = txn2.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw != nil {
		t.Fatalf("bad: %#v", raw)
	}

	// Create a new txn
	txn3 := db.Txn(false)

	// Results should show up in this transaction
	raw, err = txn3.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw == nil {
		t.Fatalf("bad: %#v", raw)
	}
}

// Test that an abort clears progress
func TestTxn_Abort(t *testing.T) {
	db := testDB(t)
	txn1 := db.Txn(true)

	obj := &TestObject{
		ID:  "my-object",
		Foo: "abc",
		Qux: []string{"abc1", "abc2"},
	}
	obj2 := &TestObject{
		ID:  "my-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}
	obj3 := &TestObject{
		ID:  "my-other-cool-thing",
		Foo: "xyz",
		Qux: []string{"xyz1", "xyz2"},
	}

	err := txn1.Insert("main", obj)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj2)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	err = txn1.Insert("main", obj3)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	// Abort the txn
	txn1.Abort()
	txn1.Commit()

	// Create a new transaction
	txn2 := db.Txn(false)

	// Nothing should show up in this transaction
	raw, err := txn2.First("main", "id")
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if raw != nil {
		t.Fatalf("bad: %#v", raw)
	}
}

func TestComplexDB(t *testing.T) {
	db := testComplexDB(t)
	testPopulateData(t, db)
	txn := db.Txn(false) // read only

	// Get using a full name
	raw, err := txn.First("people", "name", "Armon", "Dadgar")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	// Get using a prefix
	raw, err = txn.First("people", "name_prefix", "Armon")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	raw, err = txn.First("people", "id_prefix", raw.(*TestPerson).ID[:4])
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	// Get based on field set.
	result, err := txn.Get("people", "sibling", true)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	exp := map[string]bool{"Alex": true, "Armon": true}
	act := make(map[string]bool, 2)
	for i := result.Next(); i != nil; i = result.Next() {
		p, ok := i.(*TestPerson)
		if !ok {
			t.Fatalf("should get person")
		}
		act[p.First] = true
	}

	if !reflect.DeepEqual(act, exp) {
		t.Fatalf("Got %#v; want %#v", act, exp)
	}

	raw, err = txn.First("people", "sibling", false)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}
	if raw.(*TestPerson).First != "Mitchell" {
		t.Fatalf("wrong person!")
	}

	// Where in the world is mitchell hashimoto?
	raw, err = txn.First("people", "name_prefix", "Mitchell")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}

	person := raw.(*TestPerson)
	if person.First != "Mitchell" {
		t.Fatalf("wrong person!")
	}

	raw, err = txn.First("visits", "id_prefix", person.ID)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get visit")
	}

	visit := raw.(*TestVisit)

	raw, err = txn.First("places", "id", visit.Place)
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get place")
	}

	place := raw.(*TestPlace)
	if place.Name != "Maui" {
		t.Fatalf("bad place (but isn't anywhere else really?): %v", place)
	}
}

func TestWatchUpdate(t *testing.T) {
	db := testComplexDB(t)
	testPopulateData(t, db)
	txn := db.Txn(false) // read only

	watchSetSpecific := NewWatchSet()
	watchSetPrefix := NewWatchSet()

	// Get using a full name
	watch, raw, err := txn.FirstWatch("people", "name", "Armon", "Dadgar")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}
	watchSetSpecific.Add(watch)

	// Get using a prefix
	watch, raw, err = txn.FirstWatch("people", "name_prefix", "Armon")
	noErr(t, err)
	if raw == nil {
		t.Fatalf("should get person")
	}
	watchSetPrefix.Add(watch)

	txn2 := db.Txn(true) // write
	noErr(t, txn2.Delete("people", raw))
	txn2.Commit()

	// Both watches should trigger!
	timeout := time.After(time.Second)
	if timeout := watchSetSpecific.Watch(timeout); timeout {
		t.Fatalf("should not timeout")
	}
	if timeout := watchSetPrefix.Watch(timeout); timeout {
		t.Fatalf("should not timeout")
	}
}

func testPopulateData(t *testing.T, db *MemDB) {
	// Start write txn
	txn := db.Txn(true)

	// Create some data
	person1 := testPerson()

	person2 := testPerson()
	person2.First = "Mitchell"
	person2.Last = "Hashimoto"

	person3 := testPerson()
	person3.First = "Alex"
	person3.Last = "Dadgar"

	person1.Sibling = person3
	person3.Sibling = person1

	place1 := testPlace()
	place2 := testPlace()
	place2.Name = "Maui"

	visit1 := &TestVisit{person1.ID, place1.ID}
	visit2 := &TestVisit{person2.ID, place2.ID}

	// Insert it all
	noErr(t, txn.Insert("people", person1))
	noErr(t, txn.Insert("people", person2))
	noErr(t, txn.Insert("people", person3))
	noErr(t, txn.Insert("places", place1))
	noErr(t, txn.Insert("places", place2))
	noErr(t, txn.Insert("visits", visit1))
	noErr(t, txn.Insert("visits", visit2))

	// Commit
	txn.Commit()
}

func noErr(t *testing.T, err error) {
	if err != nil {
		t.Fatalf("err: %v", err)
	}
}

type TestPerson struct {
	ID      string
	First   string
	Last    string
	Sibling *TestPerson
}

type TestPlace struct {
	ID   string
	Name string
}

type TestVisit struct {
	Person string
	Place  string
}

func testComplexSchema() *DBSchema {
	return &DBSchema{
		Tables: map[string]*TableSchema{
			"people": &TableSchema{
				Name: "people",
				Indexes: map[string]*IndexSchema{
					"id": &IndexSchema{
						Name:    "id",
						Unique:  true,
						Indexer: &UUIDFieldIndex{Field: "ID"},
					},
					"name": &IndexSchema{
						Name:   "name",
						Unique: true,
						Indexer: &CompoundIndex{
							Indexes: []Indexer{
								&StringFieldIndex{Field: "First"},
								&StringFieldIndex{Field: "Last"},
							},
						},
					},
					"sibling": &IndexSchema{
						Name:    "sibling",
						Unique:  false,
						Indexer: &FieldSetIndex{Field: "Sibling"},
					},
				},
			},
			"places": &TableSchema{
				Name: "places",
				Indexes: map[string]*IndexSchema{
					"id": &IndexSchema{
						Name:    "id",
						Unique:  true,
						Indexer: &UUIDFieldIndex{Field: "ID"},
					},
					"name": &IndexSchema{
						Name:    "name",
						Unique:  true,
						Indexer: &StringFieldIndex{Field: "Name"},
					},
				},
			},
			"visits": &TableSchema{
				Name: "visits",
				Indexes: map[string]*IndexSchema{
					"id": &IndexSchema{
						Name:   "id",
						Unique: true,
						Indexer: &CompoundIndex{
							Indexes: []Indexer{
								&UUIDFieldIndex{Field: "Person"},
								&UUIDFieldIndex{Field: "Place"},
							},
						},
					},
				},
			},
		},
	}
}

func testComplexDB(t *testing.T) *MemDB {
	db, err := NewMemDB(testComplexSchema())
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	return db
}

func testPerson() *TestPerson {
	_, uuid := generateUUID()
	obj := &TestPerson{
		ID:    uuid,
		First: "Armon",
		Last:  "Dadgar",
	}
	return obj
}

func testPlace() *TestPlace {
	_, uuid := generateUUID()
	obj := &TestPlace{
		ID:   uuid,
		Name: "HashiCorp",
	}
	return obj
}
<|endoftext|>


<commit_before>package dnsp

import (
	"log"

	"github.com/miekg/dns"
)

const (
	Unknown listType = iota
	White            // whitelisted
	Black            // blacklisted
)

type listType uint8

type hosts map[string]listType

// Whitelist whitelists a hosts.
func (s *Server) Whitelist(host string) {
	setHost(s.hosts, host, White)
}

// Blacklist blacklists a host.
func (s *Server) Blacklist(host string) {
	setHost(s.hosts, host, Black)
}

func setHost(hosts map[string]listType, host string, b listType) {
	if host == "" {
		return
	}
	if host[len(host)-1] != '.' {
		host += "."
	}
	hosts[host] = b
}

// IsAllowed returns whether we are allowed to resolve this host.
//
// If the server is whitelisting, the rusilt will be true if the host is on the whitelist.
// If the server is blacklisting, the result will be true if the host is NOT on the blacklist.
//
// NOTE: "host" must end with a dot.
func (s *Server) IsAllowed(host string) bool {
	log.Printf("%s %#v", host, s.hosts)
	b := s.hosts[host]
	if s.white {
		return b == White
	}
	return b != Black
}

func (s *Server) filter(qs []dns.Question) []dns.Question {
	result := []dns.Question{}
	for _, q := range qs {
		if s.IsAllowed(q.Name) {
			result = append(result, q)
		}
	}
	return result
}

func loadWhitelist(h hosts, path string) error {
	return nil
}

func loadBlacklist(h hosts, path string) error {
	return nil
}
<commit_msg>rename listType to host<commit_after>package dnsp

import (
	"log"

	"github.com/miekg/dns"
)

const (
	Unknown host = iota
	White        // whitelisted
	Black        // blacklisted
)

type host uint8

type hosts map[string]host

// Whitelist whitelists a hosts.
func (s *Server) Whitelist(host string) {
	setHost(s.hosts, host, White)
}

// Blacklist blacklists a host.
func (s *Server) Blacklist(host string) {
	setHost(s.hosts, host, Black)
}

func setHost(hosts map[string]host, host string, b host) {
	if host == "" {
		return
	}
	if host[len(host)-1] != '.' {
		host += "."
	}
	hosts[host] = b
}

// IsAllowed returns whether we are allowed to resolve this host.
//
// If the server is whitelisting, the rusilt will be true if the host is on the whitelist.
// If the server is blacklisting, the result will be true if the host is NOT on the blacklist.
//
// NOTE: "host" must end with a dot.
func (s *Server) IsAllowed(host string) bool {
	log.Printf("%s %#v", host, s.hosts)
	b := s.hosts[host]
	if s.white {
		return b == White
	}
	return b != Black
}

func (s *Server) filter(qs []dns.Question) []dns.Question {
	result := []dns.Question{}
	for _, q := range qs {
		if s.IsAllowed(q.Name) {
			result = append(result, q)
		}
	}
	return result
}

func loadWhitelist(h hosts, path string) error {
	return nil
}

func loadBlacklist(h hosts, path string) error {
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/kidoman/embd"
)

const debounceInterval = 20 * time.Millisecond

type PI struct { // a pair of integers
	a int
	b int
}

type LocalGPIO struct {
	mutex sync.Mutex
}

// compile time check it complies with gpio interface
var _ = GPIO(&LocalGPIO{})

func (g *LocalGPIO) Init() {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()
	embd.InitGPIO()
}

func (g *LocalGPIO) SetupInput(pin int) {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()

	pname := fmt.Sprintf("P1_%d", pin)
	p, err := embd.NewDigitalPin(pname)
	if err != nil {
		fmt.Printf("Could not set %v to output: %v", pname, err)
	}
	p.SetDirection(embd.In)
	p.PullUp()

	// debouncing
	lastTrigger := time.Now()

	// Watch interrupts are all triggered on one goroutine so we're fairly safe to
	// just use Watch()
	p.Watch(embd.EdgeRising, func(p embd.DigitalPin) {
		if time.Since(lastTrigger) < debounceInterval {
			return
		}

		lastTrigger = time.Now()

		fmt.Printf("Sensor pin %v triggered.\n", pin)

	})

	fmt.Printf("Pin %v is now an input\n", pname)

}

func (g *LocalGPIO) SetupOutput(pin int) {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()
	pname := fmt.Sprintf("P1_%d", pin)
	p, err := embd.NewDigitalPin(pname)
	if err != nil {
		fmt.Printf("Could not set %v to output: %v", pname, err)
	}
	p.SetDirection(embd.Out)
	fmt.Printf("Pin %v is now an output\n", pname)
}

func (g *LocalGPIO) SetPin(pin int, value int) {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()
	pname := fmt.Sprintf("P1_%d", pin)
	var pushValue int
	if value <= 0 {
		pushValue = embd.Low
	} else {
		pushValue = embd.High
	}
	embd.DigitalWrite(pname, pushValue)
}
<commit_msg>more debug output<commit_after>package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/kidoman/embd"
)

const debounceInterval = 20 * time.Millisecond

type PI struct { // a pair of integers
	a int
	b int
}

type LocalGPIO struct {
	mutex sync.Mutex
}

// compile time check it complies with gpio interface
var _ = GPIO(&LocalGPIO{})

func (g *LocalGPIO) Init() {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()
	embd.InitGPIO()
}

func (g *LocalGPIO) SetupInput(pin int) {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()

	pname := fmt.Sprintf("P1_%d", pin)
	p, err := embd.NewDigitalPin(pname)
	if err != nil {
		fmt.Printf("Could not set %v to output: %v", pname, err)
	}
	p.SetDirection(embd.In)
	p.PullUp()

	// debouncing
	lastTrigger := time.Now()

	// Watch interrupts are all triggered on one goroutine so we're fairly safe to
	// just use Watch()
	p.Watch(embd.EdgeRising, func(p embd.DigitalPin) {
		if time.Since(lastTrigger) < debounceInterval {
			fmt.Printf("... disregarding trigger due to debounce: %v\n", lastTrigger)
			return
		}

		lastTrigger = time.Now()

		fmt.Printf("Sensor pin %v triggered.\n", pin)

	})

	fmt.Printf("Pin %v is now an input\n", pname)

}

func (g *LocalGPIO) SetupOutput(pin int) {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()
	pname := fmt.Sprintf("P1_%d", pin)
	p, err := embd.NewDigitalPin(pname)
	if err != nil {
		fmt.Printf("Could not set %v to output: %v", pname, err)
	}
	p.SetDirection(embd.Out)
	fmt.Printf("Pin %v is now an output\n", pname)
}

func (g *LocalGPIO) SetPin(pin int, value int) {
	(&g.mutex).Lock()
	defer (&g.mutex).Unlock()
	pname := fmt.Sprintf("P1_%d", pin)
	var pushValue int
	if value <= 0 {
		pushValue = embd.Low
	} else {
		pushValue = embd.High
	}
	embd.DigitalWrite(pname, pushValue)
}
<|endoftext|>


<commit_before>package memdb

import "fmt"
import "os"
import "testing"
import "time"
import "math/rand"
import "sync"
import "runtime"
import "encoding/binary"

func TestInsert(t *testing.T) {
	db := New()
	w := db.NewWriter()
	for i := 0; i < 2000; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	for i := 1750; i < 2000; i++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}
	snap := w.NewSnapshot()

	for i := 2000; i < 5000; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	_ = w.NewSnapshot()

	itr := w.NewIterator(snap)
	count := 0
	itr.SeekFirst()
	itr.Seek(NewItem([]byte(fmt.Sprintf("%010d", 1500))))
	for ; itr.Valid(); itr.Next() {
		expected := fmt.Sprintf("%010d", count+1500)
		got := string(itr.Get().data)
		count++
		if got != expected {
			t.Errorf("Expected %s, got %v", expected, got)
		}
	}

	if count != 250 {
		t.Errorf("Expected count = 250, got %v", count)
	}
}

func doInsert(db *MemDB, wg *sync.WaitGroup, n int, isRand bool, shouldSnap bool) {
	defer wg.Done()
	w := db.NewWriter()
	rnd := rand.New(rand.NewSource(int64(rand.Int())))
	for i := 0; i < n; i++ {
		var val int
		if isRand {
			val = rnd.Int()
		} else {
			val = i
		}
		if shouldSnap && i%100000 == 0 {
			s := w.NewSnapshot()
			s.Close()
		}
		buf := make([]byte, 8)
		binary.BigEndian.PutUint64(buf, uint64(val))
		// w.Put(NewItem([]byte(fmt.Sprintf("%025d", val))))
		w.Put(NewItem(buf))
	}
}

func TestInsertPerf(t *testing.T) {
	var wg sync.WaitGroup
	db := New()
	n := 1000000
	t0 := time.Now()
	total := n * runtime.GOMAXPROCS(0)
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		wg.Add(1)
		go doInsert(db, &wg, n, true, true)
	}
	wg.Wait()

	dur := time.Since(t0)
	fmt.Printf("%d items took %v -> %v items/s snapshots_created %v live_snapshots %v\n",
		total, dur, float64(total)/float64(dur.Seconds()), db.getCurrSn(), len(db.GetSnapshots()))
}

func doGet(t *testing.T, db *MemDB, snap *Snapshot, wg *sync.WaitGroup, n int) {
	defer wg.Done()
	rnd := rand.New(rand.NewSource(int64(rand.Int())))

	buf := make([]byte, 8)
	itr := db.NewIterator(snap)
	for i := 0; i < n; i++ {
		val := rnd.Int() % n
		binary.BigEndian.PutUint64(buf, uint64(val))
		// itr.Seek(NewItem([]byte(fmt.Sprintf("%025d", val))))
		itr.Seek(NewItem(buf))
		if !itr.Valid() {
			t.Errorf("Expected to find %v", val)
		}
	}
}

func TestGetPerf(t *testing.T) {
	var wg sync.WaitGroup
	db := New()
	n := 1000000
	wg.Add(1)
	go doInsert(db, &wg, n, false, true)
	wg.Wait()
	snap := db.NewSnapshot()

	t0 := time.Now()
	total := n * runtime.GOMAXPROCS(0)
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		wg.Add(1)
		go doGet(t, db, snap, &wg, n)
	}
	wg.Wait()
	dur := time.Since(t0)
	fmt.Printf("%d items took %v -> %v items/s\n", total, dur, float64(total)/float64(dur.Seconds()))
}

func CountItems(db *MemDB, snap *Snapshot) int {
	var count int
	itr := db.NewIterator(snap)
	for itr.SeekFirst(); itr.Valid(); itr.Next() {
		count++
	}
	itr.Close()
	return count
}

func TestLoadStoreDisk(t *testing.T) {
	os.RemoveAll("db.dump")
	var wg sync.WaitGroup
	cfg := DefaultConfig()
	db := NewWithConfig(cfg)
	n := 1000000
	t0 := time.Now()
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		wg.Add(1)
		go doInsert(db, &wg, n/runtime.GOMAXPROCS(0), true, true)
	}
	wg.Wait()
	fmt.Printf("Inserting %v items took %v\n", n, time.Since(t0))
	snap := db.NewSnapshot()
	snap = db.NewSnapshot()
	fmt.Println(db.DumpStats())

	t0 = time.Now()
	err := db.StoreToDisk("db.dump", snap, nil)
	if err != nil {
		t.Errorf("Expected no error. got=%v", err)
	}

	fmt.Printf("Storing to disk took %v\n", time.Since(t0))

	snap.Close()
	db = NewWithConfig(cfg)
	t0 = time.Now()
	snap, err = db.LoadFromDisk("db.dump", nil)
	if err != nil {
		t.Errorf("Expected no error. got=%v", err)
	}
	fmt.Printf("Loading from disk took %v\n", time.Since(t0))

	count := CountItems(db, snap)
	if count != n {
		t.Errorf("Expected %v, got %v", n, count)
	}
	fmt.Println(db.DumpStats())
}

func TestDelete(t *testing.T) {
	expected := 10
	db := New()
	w := db.NewWriter()
	for i := 0; i < expected; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	snap1 := w.NewSnapshot()
	got := CountItems(db, snap1)
	if got != expected {
		t.Errorf("Expected 2000, got %d", got)
	}
	fmt.Println(db.DumpStats())

	for i := 0; i < expected; i++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	for i := 0; i < expected; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}
	snap2 := w.NewSnapshot()
	snap1.Close()
	snap3 := w.NewSnapshot()
	snap2.Close()
	time.Sleep(time.Second)

	got = CountItems(db, snap3)
	snap3.Close()

	if got != expected {
		t.Errorf("Expected %d, got %d", expected, got)
	}
	fmt.Println(db.DumpStats())
}

func doReplace(wg *sync.WaitGroup, t *testing.T, w *Writer, start, end int) {
	defer wg.Done()

	for ; start < end; start++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", start))))
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", start))))
	}
}

func TestGCPerf(t *testing.T) {
	var wg sync.WaitGroup
	var last *Snapshot

	db := New()
	perW := 1000
	iterations := 1000
	nW := runtime.GOMAXPROCS(0)

	var ws []*Writer

	for i := 0; i < nW; i++ {
		ws = append(ws, db.NewWriter())
	}

	nc := 0
	for x := 0; x < iterations; x++ {
		for i := 0; i < nW; i++ {
			wg.Add(1)
			go doReplace(&wg, t, ws[i], i*perW, i*perW+perW)
		}
		wg.Wait()
		curr := db.NewSnapshot()
		if last != nil {
			last.Close()
		}

		last = curr
		nc += db.store.GetStats().NodeCount
	}

	db.NewSnapshot()
	last.Close()

	waits := 0
	for db.store.GetStats().NodeCount > nW*perW {
		time.Sleep(time.Millisecond)
		waits++
	}

	fmt.Printf("final_node_count = %v, average_live_node_count = %v, wait_time_for_collection = %vms\n", db.store.GetStats().NodeCount, nc/iterations, waits)
}

func TestMemoryInUse(t *testing.T) {
	db := New()

	dumpStats := func() {
		fmt.Printf("ItemsCount: %v, MemoryInUse: %v, NodesCount: %v\n", db.ItemsCount(), MemoryInUse(), db.store.GetStats().NodeCount)
	}
	w := db.NewWriter()
	for i := 0; i < 5000; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}
	snap1 := w.NewSnapshot()

	dumpStats()

	for i := 0; i < 5000; i++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	snap1.Close()
	snap2 := w.NewSnapshot()
	w.NewSnapshot()
	snap2.Close()
	time.Sleep(time.Second)
	dumpStats()
}
<commit_msg>memdb: Close db instances in tests<commit_after>package memdb

import "fmt"
import "os"
import "testing"
import "time"
import "math/rand"
import "sync"
import "runtime"
import "encoding/binary"

func TestInsert(t *testing.T) {
	db := New()
	defer db.Close()
	w := db.NewWriter()
	for i := 0; i < 2000; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	for i := 1750; i < 2000; i++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}
	snap := w.NewSnapshot()

	for i := 2000; i < 5000; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	_ = w.NewSnapshot()

	itr := w.NewIterator(snap)
	count := 0
	itr.SeekFirst()
	itr.Seek(NewItem([]byte(fmt.Sprintf("%010d", 1500))))
	for ; itr.Valid(); itr.Next() {
		expected := fmt.Sprintf("%010d", count+1500)
		got := string(itr.Get().data)
		count++
		if got != expected {
			t.Errorf("Expected %s, got %v", expected, got)
		}
	}

	if count != 250 {
		t.Errorf("Expected count = 250, got %v", count)
	}
}

func doInsert(db *MemDB, wg *sync.WaitGroup, n int, isRand bool, shouldSnap bool) {
	defer wg.Done()
	w := db.NewWriter()
	rnd := rand.New(rand.NewSource(int64(rand.Int())))
	for i := 0; i < n; i++ {
		var val int
		if isRand {
			val = rnd.Int()
		} else {
			val = i
		}
		if shouldSnap && i%100000 == 0 {
			s := w.NewSnapshot()
			s.Close()
		}
		buf := make([]byte, 8)
		binary.BigEndian.PutUint64(buf, uint64(val))
		// w.Put(NewItem([]byte(fmt.Sprintf("%025d", val))))
		w.Put(NewItem(buf))
	}
}

func TestInsertPerf(t *testing.T) {
	var wg sync.WaitGroup
	db := New()
	defer db.Close()
	n := 1000000
	t0 := time.Now()
	total := n * runtime.GOMAXPROCS(0)
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		wg.Add(1)
		go doInsert(db, &wg, n, true, true)
	}
	wg.Wait()

	dur := time.Since(t0)
	fmt.Printf("%d items took %v -> %v items/s snapshots_created %v live_snapshots %v\n",
		total, dur, float64(total)/float64(dur.Seconds()), db.getCurrSn(), len(db.GetSnapshots()))
}

func doGet(t *testing.T, db *MemDB, snap *Snapshot, wg *sync.WaitGroup, n int) {
	defer wg.Done()
	rnd := rand.New(rand.NewSource(int64(rand.Int())))

	buf := make([]byte, 8)
	itr := db.NewIterator(snap)
	for i := 0; i < n; i++ {
		val := rnd.Int() % n
		binary.BigEndian.PutUint64(buf, uint64(val))
		// itr.Seek(NewItem([]byte(fmt.Sprintf("%025d", val))))
		itr.Seek(NewItem(buf))
		if !itr.Valid() {
			t.Errorf("Expected to find %v", val)
		}
	}
}

func TestGetPerf(t *testing.T) {
	var wg sync.WaitGroup
	db := New()
	defer db.Close()
	n := 1000000
	wg.Add(1)
	go doInsert(db, &wg, n, false, true)
	wg.Wait()
	snap := db.NewSnapshot()

	t0 := time.Now()
	total := n * runtime.GOMAXPROCS(0)
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		wg.Add(1)
		go doGet(t, db, snap, &wg, n)
	}
	wg.Wait()
	dur := time.Since(t0)
	fmt.Printf("%d items took %v -> %v items/s\n", total, dur, float64(total)/float64(dur.Seconds()))
}

func CountItems(db *MemDB, snap *Snapshot) int {
	var count int
	itr := db.NewIterator(snap)
	for itr.SeekFirst(); itr.Valid(); itr.Next() {
		count++
	}
	itr.Close()
	return count
}

func TestLoadStoreDisk(t *testing.T) {
	os.RemoveAll("db.dump")
	var wg sync.WaitGroup
	cfg := DefaultConfig()
	db := NewWithConfig(cfg)
	defer db.Close()
	n := 1000000
	t0 := time.Now()
	for i := 0; i < runtime.GOMAXPROCS(0); i++ {
		wg.Add(1)
		go doInsert(db, &wg, n/runtime.GOMAXPROCS(0), true, true)
	}
	wg.Wait()
	fmt.Printf("Inserting %v items took %v\n", n, time.Since(t0))
	snap := db.NewSnapshot()
	snap = db.NewSnapshot()
	fmt.Println(db.DumpStats())

	t0 = time.Now()
	err := db.StoreToDisk("db.dump", snap, nil)
	if err != nil {
		t.Errorf("Expected no error. got=%v", err)
	}

	fmt.Printf("Storing to disk took %v\n", time.Since(t0))

	snap.Close()
	db = NewWithConfig(cfg)
	defer db.Close()
	t0 = time.Now()
	snap, err = db.LoadFromDisk("db.dump", nil)
	if err != nil {
		t.Errorf("Expected no error. got=%v", err)
	}
	fmt.Printf("Loading from disk took %v\n", time.Since(t0))

	count := CountItems(db, snap)
	if count != n {
		t.Errorf("Expected %v, got %v", n, count)
	}
	fmt.Println(db.DumpStats())
}

func TestDelete(t *testing.T) {
	expected := 10
	db := New()
	defer db.Close()
	w := db.NewWriter()
	for i := 0; i < expected; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	snap1 := w.NewSnapshot()
	got := CountItems(db, snap1)
	if got != expected {
		t.Errorf("Expected 2000, got %d", got)
	}
	fmt.Println(db.DumpStats())

	for i := 0; i < expected; i++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	for i := 0; i < expected; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}
	snap2 := w.NewSnapshot()
	snap1.Close()
	snap3 := w.NewSnapshot()
	snap2.Close()
	time.Sleep(time.Second)

	got = CountItems(db, snap3)
	snap3.Close()

	if got != expected {
		t.Errorf("Expected %d, got %d", expected, got)
	}
	fmt.Println(db.DumpStats())
}

func doReplace(wg *sync.WaitGroup, t *testing.T, w *Writer, start, end int) {
	defer wg.Done()

	for ; start < end; start++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", start))))
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", start))))
	}
}

func TestGCPerf(t *testing.T) {
	var wg sync.WaitGroup
	var last *Snapshot

	db := New()
	defer db.Close()
	perW := 1000
	iterations := 1000
	nW := runtime.GOMAXPROCS(0)

	var ws []*Writer

	for i := 0; i < nW; i++ {
		ws = append(ws, db.NewWriter())
	}

	nc := 0
	for x := 0; x < iterations; x++ {
		for i := 0; i < nW; i++ {
			wg.Add(1)
			go doReplace(&wg, t, ws[i], i*perW, i*perW+perW)
		}
		wg.Wait()
		curr := db.NewSnapshot()
		if last != nil {
			last.Close()
		}

		last = curr
		nc += db.store.GetStats().NodeCount
	}

	db.NewSnapshot()
	last.Close()

	waits := 0
	for db.store.GetStats().NodeCount > nW*perW {
		time.Sleep(time.Millisecond)
		waits++
	}

	fmt.Printf("final_node_count = %v, average_live_node_count = %v, wait_time_for_collection = %vms\n", db.store.GetStats().NodeCount, nc/iterations, waits)
}

func TestMemoryInUse(t *testing.T) {
	db := New()
	defer db.Close()

	dumpStats := func() {
		fmt.Printf("ItemsCount: %v, MemoryInUse: %v, NodesCount: %v\n", db.ItemsCount(), MemoryInUse(), db.store.GetStats().NodeCount)
	}
	w := db.NewWriter()
	for i := 0; i < 5000; i++ {
		w.Put(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}
	snap1 := w.NewSnapshot()

	dumpStats()

	for i := 0; i < 5000; i++ {
		w.Delete(NewItem([]byte(fmt.Sprintf("%010d", i))))
	}

	snap1.Close()
	snap2 := w.NewSnapshot()
	w.NewSnapshot()
	snap2.Close()
	time.Sleep(time.Second)
	dumpStats()
}
<|endoftext|>