<commit_before>package main

import (
	"code.google.com/p/go9p/p"
	"code.google.com/p/go9p/p/srv"
	"strings"
	"errors"
	"bytes"
	"time"
	"flag"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"strconv"
	"unicode/utf8"
)

type M9Player struct {
	playlist []string
	position int
	queue []string

	player *M9Play
	song *string

	spawn chan string
}

type M9Play struct {
	Song string
	proc *os.Process
	killed bool
	reaped bool
}

func (player *M9Play) Kill() {
	if player.killed {
		return
	}
	player.killed = true
	go func() {
		/* we want to make sure the process dies, but give it a chance
		 * to go to its death gracefully before pulling out SIGKILL */
		for i := 0; i < 5; i++ {
			player.proc.Signal(os.Interrupt)
			time.Sleep(25 * time.Millisecond)
			if player.reaped {
				return
			}
		}
		player.proc.Kill()
	}()
}

func (player *M9Play) Reap(m9 *M9Player) {
	player.proc.Wait()
	player.reaped = true
	if player.killed {
		return
	}
	/* player terminated normally, kickoff next song */
	if len(m9.queue) == 0 && len(m9.playlist) > 0 {
		m9.position = (m9.position + 1) % len(m9.playlist)
	}
	m9.Play("")
}

var m9 *M9Player

func NewM9Player() *M9Player {
	m9 := M9Player{spawn: make(chan string, 15)}
	go m9.spawner()
	return &m9
}

func (m9 *M9Player) _spawn(song string) *M9Play {
	path, err := exec.LookPath("m9play")
	if err != nil {
		fmt.Printf("couldn't find m9play: %s\n", err)
		return nil
	}
	proc, err := os.StartProcess(path, []string{"m9play", song}, new(os.ProcAttr))
	if err != nil {
		fmt.Printf("couldn't spawn player: %s\n", err)
		return nil
	}
	return &M9Play{song, proc, false, false}
}

func (m9 *M9Player) spawner() {
	for {
		song := <-m9.spawn
		if m9.player != nil {
			/* already playing; stop the current player first */
			m9.player.Kill()
		}
		player := m9._spawn(song)
		if player == nil {
			m9.player = nil
			events <- m9.state()
			continue
		}
		go player.Reap(m9)
		m9.player = player
		events <- "Play " + song
	}
}

/* instantaneous state */
func (m9 *M9Player) state() string {
	player := m9.player
	if player != nil {
		return "Play " + player.Song
	} else {
		if len(m9.queue) > 0 {
			return "Stop " + m9.queue[0]
		} else if len(m9.playlist) > 0 {
			return "Stop " + m9.playlist[m9.position]
		}
		return "Stop"
	}
}

func (m9 *M9Player) Add(song string) {
	m9.playlist = append(m9.playlist, song)
	if m9.player == nil && len(m9.queue) == 0 && len(m9.playlist) == 1 {
		/* We are stopped, the queue is empty and this was the first song added to the list.
		 * Update /event to reflect the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Clear() {
	m9.playlist = make([]string, 0)
}

func (m9 *M9Player) Enqueue(song string) {
	m9.queue = append(m9.queue, song)
	if m9.player != nil && len(m9.queue) == 1 {
		/* We are stopped, and this was the first item added to the queue.
		 * Update /event to show the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Play(song string) {
	if song != "" {
		m9.spawn <- song
	} else {
		if len(m9.queue) > 0 {
			m9.spawn <- m9.queue[0]
			m9.queue = m9.queue[1:]
		} else if len(m9.playlist) > 0 {
			m9.spawn <- m9.playlist[m9.position]
		}
	}
}

func (m9 *M9Player) Skip(n int) {
	if len(m9.playlist) == 0 {
		return
	}
	m9.position += n
	m9.position %= len(m9.playlist)
	if m9.position < 0 {
		m9.position += len(m9.playlist)
	}
	if m9.player != nil {
		m9.Play("")
	} else {
		events <- m9.state()
	}
}

func (m9 *M9Player) Stop() {
	player := m9.player
	m9.player = nil
	if player != nil {
		player.Kill()
	}
	events <- m9.state()
}

func skip(amount string) error {
	if amount == "" {
		m9.Skip(1)
		return nil
	}
	i, err := strconv.Atoi(amount)
	if err != nil {
		return err
	}
	m9.Skip(i)
	return nil
}

var events chan string
var register chan chan string

func eventer() {
	listeners := make([]chan string, 0)
	for {
		select {
		case ev := <- events:
			for i := range(listeners) {
				listeners[i] <- ev
			}
			listeners = make([]chan string, 0)

		case l := <- register:
			listeners = append(listeners, l)
		}
	}
}

func waitForEvent() string {
	c := make(chan string)
	register <- c
	ev := <- c
	return ev
}

var ntype = flag.String("net", "unix", "network type")
var addr = flag.String("addr", "/tmp/ns.sqweek.:0/m9u", "network address")

type CtlFile struct {
	srv.File
}
type SongListFile struct {
	srv.File
	rd map[*srv.Fid] []byte
	wr map[*srv.Fid] *PartialLine
	SongAdded func(string)
}
type ListFile struct {
	SongListFile
}
type QueueFile struct {
	SongListFile
}
type EventFile struct {
	srv.File
}

func (*CtlFile) Write(fid *srv.FFid, b []byte, offset uint64) (n int, err error) {
	cmd := string(b)
	if strings.HasPrefix(cmd, "play") {
		m9.Play(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "skip") {
		err = skip(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "stop") {
		m9.Stop()
	} else {
		err = errors.New("ill-formed control message")
	}
	if err == nil {
		n = len(b)
	}
	return n, err
}

func (*CtlFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	return nil
}

func mkbuf(lst []string) []byte {
	buflen := 0
	for i := range(lst) {
		buflen += 1 + len([]byte(lst[i]))
	}
	buf := make([]byte, buflen)
	j := 0
	for i := range(lst) {
		j += copy(buf[j:], lst[i])
		buf[j] = '\n'
		j++
	}
	return buf
}

type PartialLine struct {
	leftover []byte
}

func (part *PartialLine) append(bytes []byte) string {
	if part.leftover == nil {
		return string(bytes)
	}
	left := part.leftover
	part.leftover = nil
	return string(left) + string(bytes)
}

func (lstfile *ListFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		lstfile.wr[fid.Fid] = new(PartialLine)
		if mode & p.OTRUNC != 0 {
			m9.Clear()
		}
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		lstfile.rd[fid.Fid] = mkbuf(m9.playlist)
	}
	return nil
}

func (slf *SongListFile) Write(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	prefix, ok := slf.wr[fid.Fid]
	if !ok {
		return 0, errors.New("internal state corrupted")
	}
	i := 0
	for {
		j := bytes.IndexByte(b[i:], '\n')
		if j == -1 {
			break
		}
		song := prefix.append(b[i:i+j])
		slf.SongAdded(song)
		//m9.Add(song)
		i += j+1
	}
	if i < len(b) {
		prefix.leftover = b[i:]
	}
	return len(b), nil
}

func (slf *SongListFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	return nil
}

func min(a uint64, b uint64) uint64 {
	if a < b {
		return a
	}
	return b
}

func (slf *SongListFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	buf, ok := slf.rd[fid.Fid]
	if !ok {
		return 0, errors.New("internal state corrupted")
	}
	remaining := uint64(len(buf)) - offset
	n := min(remaining, uint64(len(b)))
	copy(b, buf[offset:offset + n])
	return int(n), nil
}

func (slf *SongListFile) Clunk(fid *srv.FFid) error {
	delete(slf.rd, fid.Fid)
	delete(slf.wr, fid.Fid)
	return nil
}

func (qf *QueueFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		qf.wr[fid.Fid] = new(PartialLine)
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		qf.rd[fid.Fid] = mkbuf(m9.queue)
	}
	return nil
}

func (*EventFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	var ev string
	if offset == 0 {
		ev = m9.state()
	} else {
		ev = waitForEvent()
	}
	buf := []byte(ev)
	for len(buf) > len(b) - 1 {
		_, size := utf8.DecodeLastRune(buf)
		buf = buf[:len(buf)-size]
	}
	copy(b[:len(buf)], buf)
	b[len(buf)] = byte('\n')
	return len(buf)+1, nil
}

func (*EventFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
	return nil
}

func (slf *SongListFile) init(f func(string)) {
	slf.wr = make(map[*srv.Fid]*PartialLine)
	slf.rd = make(map[*srv.Fid] []byte)
	slf.SongAdded = f
}

func main() {
	var err error
	flag.Parse()

	uid := p.OsUsers.Uid2User(os.Geteuid())
	gid := p.OsUsers.Gid2Group(os.Getegid())
	fmt.Printf("uid = %d  gid = %d\n", os.Geteuid(), os.Getegid())

	events = make(chan string)
	register = make(chan chan string)

	m9 = NewM9Player()

	go eventer()

	root := new(srv.File)
	root.Add(nil, "/", uid, gid, p.DMDIR|0555, nil)
	ctl := new(CtlFile)
	ctl.Add(root, "ctl", uid, gid, 0644, ctl)
	list := new(ListFile)
	list.init(func(song string) {m9.Add(song)})
	list.Add(root, "list", uid, gid, 0644, list)
	queue := new(QueueFile)
	queue.init(func(song string) {m9.Enqueue(song)})
	queue.Add(root, "queue", uid, gid, 0644, queue)
	event := new(EventFile)
	event.Add(root, "event", uid, gid, 0444, event)

	s := srv.NewFileSrv(root)

	listener, err := net.Listen(*ntype, *addr)
	if err != nil {
		fmt.Printf("listen failed: %s\n", err)
		os.Exit(1)
	}
	defer listener.Close()
	sigchan := make(chan os.Signal)
	go func() {
		<- sigchan
		listener.Close()
		os.Exit(1)
	}()
	signal.Notify(sigchan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)

	s.Start(s)
	err = s.StartListener(listener)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
	}

	return
}

<commit_msg>m9u.go: Fix lost data with long lines<commit_after>package main

import (
	"code.google.com/p/go9p/p"
	"code.google.com/p/go9p/p/srv"
	"strings"
	"errors"
	"bytes"
	"time"
	"flag"
	"fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"syscall"
	"strconv"
	"unicode/utf8"
)

type M9Player struct {
	playlist []string
	position int
	queue []string

	player *M9Play
	song *string

	spawn chan string
}

type M9Play struct {
	Song string
	proc *os.Process
	killed bool
	reaped bool
}

func (player *M9Play) Kill() {
	if player.killed {
		return
	}
	player.killed = true
	go func() {
		/* we want to make sure the process dies, but give it a chance
		 * to go to its death gracefully before pulling out SIGKILL */
		for i := 0; i < 5; i++ {
			player.proc.Signal(os.Interrupt)
			time.Sleep(25 * time.Millisecond)
			if player.reaped {
				return
			}
		}
		player.proc.Kill()
	}()
}

func (player *M9Play) Reap(m9 *M9Player) {
	player.proc.Wait()
	player.reaped = true
	if player.killed {
		return
	}
	/* player terminated normally, kickoff next song */
	if len(m9.queue) == 0 && len(m9.playlist) > 0 {
		m9.position = (m9.position + 1) % len(m9.playlist)
	}
	m9.Play("")
}

var m9 *M9Player

func NewM9Player() *M9Player {
	m9 := M9Player{spawn: make(chan string, 15)}
	go m9.spawner()
	return &m9
}

func (m9 *M9Player) _spawn(song string) *M9Play {
	path, err := exec.LookPath("m9play")
	if err != nil {
		fmt.Printf("couldn't find m9play: %s\n", err)
		return nil
	}
	proc, err := os.StartProcess(path, []string{"m9play", song}, new(os.ProcAttr))
	if err != nil {
		fmt.Printf("couldn't spawn player: %s\n", err)
		return nil
	}
	return &M9Play{song, proc, false, false}
}

func (m9 *M9Player) spawner() {
	for {
		song := <-m9.spawn
		if m9.player != nil {
			/* already playing; stop the current player first */
			m9.player.Kill()
		}
		player := m9._spawn(song)
		if player == nil {
			m9.player = nil
			events <- m9.state()
			continue
		}
		go player.Reap(m9)
		m9.player = player
		events <- "Play " + song
	}
}

/* instantaneous state */
func (m9 *M9Player) state() string {
	player := m9.player
	if player != nil {
		return "Play " + player.Song
	} else {
		if len(m9.queue) > 0 {
			return "Stop " + m9.queue[0]
		} else if len(m9.playlist) > 0 {
			return "Stop " + m9.playlist[m9.position]
		}
		return "Stop"
	}
}

func (m9 *M9Player) Add(song string) {
	m9.playlist = append(m9.playlist, song)
	if m9.player == nil && len(m9.queue) == 0 && len(m9.playlist) == 1 {
		/* We are stopped, the queue is empty and this was the first song added to the list.
		 * Update /event to reflect the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Clear() {
	m9.playlist = make([]string, 0)
}

func (m9 *M9Player) Enqueue(song string) {
	m9.queue = append(m9.queue, song)
	if m9.player != nil && len(m9.queue) == 1 {
		/* We are stopped, and this was the first item added to the queue.
		 * Update /event to show the correct next-song */
		events <- m9.state()
	}
}

func (m9 *M9Player) Play(song string) {
	if song != "" {
		m9.spawn <- song
	} else {
		if len(m9.queue) > 0 {
			m9.spawn <- m9.queue[0]
			m9.queue = m9.queue[1:]
		} else if len(m9.playlist) > 0 {
			m9.spawn <- m9.playlist[m9.position]
		}
	}
}

func (m9 *M9Player) Skip(n int) {
	if len(m9.playlist) == 0 {
		return
	}
	m9.position += n
	m9.position %= len(m9.playlist)
	if m9.position < 0 {
		m9.position += len(m9.playlist)
	}
	if m9.player != nil {
		m9.Play("")
	} else {
		events <- m9.state()
	}
}

func (m9 *M9Player) Stop() {
	player := m9.player
	m9.player = nil
	if player != nil {
		player.Kill()
	}
	events <- m9.state()
}

func skip(amount string) error {
	if amount == "" {
		m9.Skip(1)
		return nil
	}
	i, err := strconv.Atoi(amount)
	if err != nil {
		return err
	}
	m9.Skip(i)
	return nil
}

var events chan string
var register chan chan string

func eventer() {
	listeners := make([]chan string, 0)
	for {
		select {
		case ev := <- events:
			for i := range(listeners) {
				listeners[i] <- ev
			}
			listeners = make([]chan string, 0)

		case l := <- register:
			listeners = append(listeners, l)
		}
	}
}

func waitForEvent() string {
	c := make(chan string)
	register <- c
	ev := <- c
	return ev
}

var ntype = flag.String("net", "unix", "network type")
var addr = flag.String("addr", "/tmp/ns.sqweek.:0/m9u", "network address")

type CtlFile struct {
	srv.File
}
type SongListFile struct {
	srv.File
	rd map[*srv.Fid] []byte
	wr map[*srv.Fid] *PartialLine
	SongAdded func(string)
}
type ListFile struct {
	SongListFile
}
type QueueFile struct {
	SongListFile
}
type EventFile struct {
	srv.File
}

func (*CtlFile) Write(fid *srv.FFid, b []byte, offset uint64) (n int, err error) {
	cmd := string(b)
	if strings.HasPrefix(cmd, "play") {
		m9.Play(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "skip") {
		err = skip(strings.Trim(cmd[4:], " \n"))
	} else if strings.HasPrefix(cmd, "stop") {
		m9.Stop()
	} else {
		err = errors.New("ill-formed control message")
	}
	if err == nil {
		n = len(b)
	}
	return n, err
}

func (*CtlFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	return nil
}

func mkbuf(lst []string) []byte {
	buflen := 0
	for i := range(lst) {
		buflen += 1 + len([]byte(lst[i]))
	}
	buf := make([]byte, buflen)
	j := 0
	for i := range(lst) {
		j += copy(buf[j:], lst[i])
		buf[j] = '\n'
		j++
	}
	return buf
}

type PartialLine struct {
	leftover []byte
}

func (part *PartialLine) append(bytes []byte) string {
	if part.leftover == nil {
		return string(bytes)
	}
	left := part.leftover
	part.leftover = nil
	return string(append(left, bytes...))
}

func (lstfile *ListFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		lstfile.wr[fid.Fid] = new(PartialLine)
		if mode & p.OTRUNC != 0 {
			m9.Clear()
		}
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		lstfile.rd[fid.Fid] = mkbuf(m9.playlist)
	}
	return nil
}

func (lstfile *ListFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	if d.Length == 0 {
		m9.Clear()
	}
	return nil
}

func (slf *SongListFile) Write(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	prefix, ok := slf.wr[fid.Fid]
	if !ok {
		return 0, errors.New("internal state corrupted")
	}
	i := 0
	for i < len(b) {
		j := bytes.IndexByte(b[i:], '\n')
		if j == -1 {
			break
		}
		song := prefix.append(b[i:i+j])
		slf.SongAdded(song)
		i += j+1
	}
	if i < len(b) {
		prefix.leftover = append(prefix.leftover, b[i:]...)
	}
	return len(b), nil
}

func (queue *QueueFile) Wstat(fid *srv.FFid, d *p.Dir) error {
	return nil
}

func min(a uint64, b uint64) uint64 {
	if a < b {
		return a
	}
	return b
}

func (slf *SongListFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	buf, ok := slf.rd[fid.Fid]
	if !ok {
		return 0, errors.New("internal state corrupted")
	}
	remaining := uint64(len(buf)) - offset
	n := min(remaining, uint64(len(b)))
	copy(b, buf[offset:offset + n])
	return int(n), nil
}

func (slf *SongListFile) Clunk(fid *srv.FFid) error {
	delete(slf.rd, fid.Fid)
	delete(slf.wr, fid.Fid)
	return nil
}

func (qf *QueueFile) Open(fid *srv.FFid, mode uint8) error {
	if mode & 3 == p.OWRITE || mode & 3 == p.ORDWR {
		qf.wr[fid.Fid] = new(PartialLine)
	}
	if mode & 3 == p.OREAD || mode & 3 == p.ORDWR {
		qf.rd[fid.Fid] = mkbuf(m9.queue)
	}
	return nil
}

func (*EventFile) Read(fid *srv.FFid, b []byte, offset uint64) (int, error) {
	var ev string
	if offset == 0 {
		ev = m9.state()
	} else {
		ev = waitForEvent()
	}
	buf := []byte(ev)
	for len(buf) > len(b) - 1 {
		_, size := utf8.DecodeLastRune(buf)
		buf = buf[:len(buf)-size]
	}
	copy(b[:len(buf)], buf)
	b[len(buf)] = byte('\n')
	return len(buf)+1, nil
}

func (*EventFile) Wstat(fid *srv.FFid, dir *p.Dir) error {
	return nil
}

func (slf *SongListFile) init(f func(string)) {
	slf.wr = make(map[*srv.Fid]*PartialLine)
	slf.rd = make(map[*srv.Fid] []byte)
	slf.SongAdded = f
}

func main() {
	var err error
	flag.Parse()

	uid := p.OsUsers.Uid2User(os.Geteuid())
	gid := p.OsUsers.Gid2Group(os.Getegid())
	fmt.Printf("uid = %d  gid = %d\n", os.Geteuid(), os.Getegid())

	events = make(chan string)
	register = make(chan chan string)

	m9 = NewM9Player()

	go eventer()

	root := new(srv.File)
	root.Add(nil, "/", uid, gid, p.DMDIR|0555, nil)
	ctl := new(CtlFile)
	ctl.Add(root, "ctl", uid, gid, 0644, ctl)
	list := new(ListFile)
	list.init(func(song string) {m9.Add(song)})
	list.Add(root, "list", uid, gid, 0644, list)
	queue := new(QueueFile)
	queue.init(func(song string) {m9.Enqueue(song)})
	queue.Add(root, "queue", uid, gid, 0644, queue)
	event := new(EventFile)
	event.Add(root, "event", uid, gid, 0444, event)

	s := srv.NewFileSrv(root)

	listener, err := net.Listen(*ntype, *addr)
	if err != nil {
		fmt.Printf("listen failed: %s\n", err)
		os.Exit(1)
	}
	defer listener.Close()
	sigchan := make(chan os.Signal)
	go func() {
		<- sigchan
		listener.Close()
		os.Exit(1)
	}()
	signal.Notify(sigchan, os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)

	s.Start(s)
	err = s.StartListener(listener)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
	}

	return
}

<|endoftext|>


<commit_before>package alert

import (
	"bytes"
	"net/http"
	"io/ioutil"
	"github.com/boivie/lovebeat/config"
	"github.com/boivie/lovebeat/service"
	"github.com/franela/goreq"
	"text/template"
	"time"
	"net/url"
)

type slackhook struct {
	Uri  string
	Data service.ViewStateChangedEvent
}

type slackhookAlerter struct {
	cmds chan slackhook
}

func (m slackhookAlerter) Notify(cfg config.ConfigAlert, ev service.ViewStateChangedEvent) {

	if cfg.Slackhook != "" {

		m.cmds <- slackhook{Uri: cfg.Slackhook, Data: ev }
	}

}

func (m slackhookAlerter) Worker(q chan slackhook, cfg *config.ConfigSlackhook) {
	for {

		select {
		case slackhook := <-q:

			var err error

			var context = make(map[string]interface{})
			context["View"] = slackhook.Data.View
			context["Previous"] = slackhook.Data.Previous
			context["Current"] = slackhook.Data.Current

			tmpl := cfg.Template

			t, err := template.New("template").Parse(tmpl)
			if err != nil {
				log.Error("error trying to parse slackhook template:%s:err:%v:", tmpl, err)
				return
			}
			var doc bytes.Buffer

			err = t.Execute(&doc, context)
			if err != nil {
				log.Error("Failed to render template", err)
				return
			}

			req := goreq.Request{
			    Method: "POST",
				Uri:         cfg.Uri,
				Accept:      "*/*",
				ContentType: "application/x-www-form-urlencoded",
				UserAgent:   "Lovebeat",
				Timeout:     10 * time.Second,
				Body:        "payload=" + url.QueryEscape(doc.String()),
			}

			req.AddHeader("X-Lovebeat", "1")

			res, err := req.Do()

			if err != nil {
				log.Error("Failed to post slackhook:%v:", err)
			}

			robots, err := ioutil.ReadAll(res.Body)
			res.Body.Close()

			//it returned a 200 so ignore any error here
			if err != nil {
				log.Error("OK:unreadable response:%v:", err)
			} else if res.StatusCode != http.StatusOK {
				log.Error("NOK:non-200:%d:", res.StatusCode)
			} else {
				log.Info("OK:response:%s:", string(robots))
			}

		}
	}

}

func NewSlackhookAlerter(cfg config.Config) Alerter {
	goreq.SetConnectTimeout(5 * time.Second)
	var q = make(chan slackhook, 100)
	var w = slackhookAlerter{cmds: q}
	go w.Worker(q, &cfg.Slackhook)
	return &w
}
<commit_msg>gofmt<commit_after>package alert

import (
	"bytes"
	"github.com/boivie/lovebeat/config"
	"github.com/boivie/lovebeat/service"
	"github.com/franela/goreq"
	"io/ioutil"
	"net/http"
	"net/url"
	"text/template"
	"time"
)

type slackhook struct {
	Uri  string
	Data service.ViewStateChangedEvent
}

type slackhookAlerter struct {
	cmds chan slackhook
}

func (m slackhookAlerter) Notify(cfg config.ConfigAlert, ev service.ViewStateChangedEvent) {

	if cfg.Slackhook != "" {

		m.cmds <- slackhook{Uri: cfg.Slackhook, Data: ev}
	}

}

func (m slackhookAlerter) Worker(q chan slackhook, cfg *config.ConfigSlackhook) {
	for {

		select {
		case slackhook := <-q:

			var err error

			var context = make(map[string]interface{})
			context["View"] = slackhook.Data.View
			context["Previous"] = slackhook.Data.Previous
			context["Current"] = slackhook.Data.Current

			tmpl := cfg.Template

			t, err := template.New("template").Parse(tmpl)
			if err != nil {
				log.Error("error trying to parse slackhook template:%s:err:%v:", tmpl, err)
				return
			}
			var doc bytes.Buffer

			err = t.Execute(&doc, context)
			if err != nil {
				log.Error("Failed to render template", err)
				return
			}

			req := goreq.Request{
				Method:      "POST",
				Uri:         cfg.Uri,
				Accept:      "*/*",
				ContentType: "application/x-www-form-urlencoded",
				UserAgent:   "Lovebeat",
				Timeout:     10 * time.Second,
				Body:        "payload=" + url.QueryEscape(doc.String()),
			}

			req.AddHeader("X-Lovebeat", "1")

			res, err := req.Do()

			if err != nil {
				log.Error("Failed to post slackhook:%v:", err)
			}

			robots, err := ioutil.ReadAll(res.Body)
			res.Body.Close()

			//it returned a 200 so ignore any error here
			if err != nil {
				log.Error("OK:unreadable response:%v:", err)
			} else if res.StatusCode != http.StatusOK {
				log.Error("NOK:non-200:%d:", res.StatusCode)
			} else {
				log.Info("OK:response:%s:", string(robots))
			}

		}
	}

}

func NewSlackhookAlerter(cfg config.Config) Alerter {
	goreq.SetConnectTimeout(5 * time.Second)
	var q = make(chan slackhook, 100)
	var w = slackhookAlerter{cmds: q}
	go w.Worker(q, &cfg.Slackhook)
	return &w
}
<|endoftext|>


<commit_before>// Fully persistent data structures. A persistent data structure is a data
// structure that always preserves the previous version of itself when
// it is modified. Such data structures are effectively immutable,
// as their operations do not update the structure in-place, but instead
// always yield a new structure.
//
// Persistent
// data structures typically share structure among themselves.  This allows
// operations to avoid copying the entire data structure.
package ps

import . "fmt"

import "bytes"

// Any is a shorthand for Go's verbose interface{} type.
type Any interface{}

// A Map associates unique keys (type string) with values (type Any).
type Map interface {
    // IsNil returns true if the Map is empty
    IsNil() bool

    // Set returns a new map in which key and value are associated.
    // If the key didn't exist before, it's created; otherwise, the
    // associated value is changed.
    // This operation is O(log N) in the number of keys.
    Set(key string, value Any) Map

    // Delete returns a new map with the association for key, if any, removed.
    // This operation is O(log N) in the number of keys.
    Delete(key string) Map

    // Lookup returns the value associated with a key, if any.  If the key
    // exists, the second return value is true; otherwise, false.
    // This operation is O(log N) in the number of keys.
    Lookup(key string) (Any, bool)

    // Size returns the number of key value pairs in the map.
    // This takes O(1) time.
    Size() int

    // ForEach executes a callback on each key value pair in the map.
    ForEach(f func(key string, val Any))

    // Keys returns a slice with all keys in this map.
    // This operation is O(N) in the number of keys.
    Keys() []string

    String() string
}

// Immutable (i.e. persistent) associative array
const childCount = 2
const shiftSize  = 1
type tree struct {
    count   int
    hash    uint64  // hash of the key (used for tree balancing)
    key     string
    value   Any
    children  [childCount]*tree
}
var nilMap = &tree{}

// Recursively set nilMap's subtrees to point at itself.
// This eliminates all nil pointers in the map structure.
// All map nodes are created by cloning this structure so
// they avoid the problem too.
func init () {
    for i := range nilMap.children {
        nilMap.children[i] = nilMap
    }
}

// NewMap allocates a new, persistent map from strings to values of
// any type.
// This is currently implemented as a path-copying binary tree.
func NewMap() Map {
    return nilMap
}

func (self *tree) IsNil() bool {
    return self == nilMap
}

// clone returns an exact duplicate of a tree node
func (self *tree) clone() *tree {
    var m tree
    m = *self
    return &m
}

// constants for FNV-1a hash algorithm
const (
    offset64 uint64 = 14695981039346656037
    prime64  uint64 = 1099511628211
)
// hashKey returns a hash code for a given string
func hashKey(key string) uint64 {
    bytes := []byte(key)
    hash := offset64
    for _, byte := range bytes {
        hash ^= uint64(byte)
        hash *= prime64
    }
    return hash
}

// Set returns a new map similar to this one but with key and value
// associated.  If the key didn't exist, it's created; otherwise, the
// associated value is changed.
func (self *tree) Set(key string, value Any) Map {
    hash := hashKey(key)
    return setLowLevel(self, hash, hash, key, value)
}

func setLowLevel(self *tree, partialHash, hash uint64, key string, value Any) *tree {
    if self.IsNil() { // an empty tree is easy
        m := self.clone()
        m.count = 1
        m.hash  = hash
        m.key   = key
        m.value = value
        return m
    }

    if hash != self.hash {
        m := self.clone()
        i := partialHash % childCount
        m.children[i] = setLowLevel(self.children[i], partialHash>>shiftSize, hash, key, value)
        recalculateCount(m)
        return m
    }

    // replacing a key's previous value
    m := self.clone()
    m.value = value
    return m
}

// modifies a map by recalculating its key count based on the counts
// of its subtrees
func recalculateCount(m *tree) {
    count := 0
    for _, t := range m.children {
        count += t.Size()
    }
    m.count = count + 1 // add one to count ourself
}

func (m *tree) Delete(key string) Map {
    hash := hashKey(key)
    newMap, _ := deleteLowLevel(m, hash, hash)
    return newMap
}

func deleteLowLevel(self *tree, partialHash, hash uint64) (*tree, bool) {
    // empty trees are easy
    if self.IsNil() {
        return self, false
    }

    if hash != self.hash {
        i := partialHash % childCount
        child, found := deleteLowLevel(self.children[i], partialHash>>shiftSize, hash)
        if !found {
            return self, false
        }
        newMap := self.clone()
        newMap.children[i] = child
        recalculateCount(newMap)
        return newMap, true   // ? this wasn't in the original code
    }

    // we must delete our own node
    if self.isLeaf() {  // we have no children
        return nilMap, true
    }
    /*
    if self.subtreeCount() == 1 { // only one subtree
        for _, t := range self.children {
            if t != nilMap {
                return t, true
            }
        }
        panic("Tree with 1 subtree actually had no subtrees")
    }
    */

    // find a node to replace us
    i := -1
    size := -1
    for j, t := range self.children {
        if t.Size() > size {
            i = j
            size = t.Size()
        }
    }

    // make chosen leaf smaller
    replacement, child := self.children[i].deleteLeftmost()
    newMap := replacement.clone()
    for j := range self.children {
        if j == i {
            newMap.children[j] = child
        } else {
            newMap.children[j] = self.children[j]
        }
    }
    recalculateCount(newMap)
    return newMap, true
}

// delete the leftmost node in a tree returning the node that
// was deleted and the tree left over after its deletion
func (m *tree) deleteLeftmost() (*tree, *tree) {
    if m.isLeaf() {
        return m, nilMap
    }

    for i, t := range m.children {
        if t != nilMap {
            deleted, child := t.deleteLeftmost()
            newMap := m.clone()
            newMap.children[i] = child
            recalculateCount(newMap)
            return deleted, newMap
        }
    }
    panic("Tree isn't a leaf but also had no children. How does that happen?")
}

// isLeaf returns true if this is a leaf node
func (m *tree) isLeaf() bool {
    return m.Size() == 1
}

// returns the number of child subtrees we have
func (m *tree) subtreeCount() int {
    count := 0
    for _, t := range m.children {
        if t != nilMap {
            count++
        }
    }
    return count
}

func (m *tree) Lookup(key string) (Any, bool) {
    hash := hashKey(key)
    return lookupLowLevel(m, hash, hash)
}

func lookupLowLevel(self *tree, partialHash, hash uint64) (Any, bool) {
    if self.IsNil() { // an empty tree is easy
        return nil, false
    }

    if hash != self.hash {
        i := partialHash % childCount
        return lookupLowLevel(self.children[i], partialHash>>shiftSize, hash)
    }

    // we found it
    return self.value, true
}

func (m *tree) Size() int {
    return m.count
}

func (m *tree) ForEach(f func(key string, val Any)) {
    if m.IsNil() {
        return
    }

    // ourself
    f(m.key, m.value)

    // children
    for _, t := range m.children {
        if t != nilMap {
            t.ForEach(f)
        }
    }
}

func (m *tree) Keys() []string {
    keys := make([]string, m.Size())
    i := 0
    m.ForEach( func (k string, v Any) {
        keys[i] = k
        i++
    })
    return keys
}

// make it easier to display maps for debugging
func (m *tree) String() string {
    keys := m.Keys()
    buf := bytes.NewBufferString("{")
    for _, key := range keys {
        val, _ := m.Lookup(key)
        Fprintf(buf, "%s: %s, ", key, val)
    }
    Fprintf(buf, "}\n")
    return buf.String()
}
<commit_msg>Use 8 children per node<commit_after>// Fully persistent data structures. A persistent data structure is a data
// structure that always preserves the previous version of itself when
// it is modified. Such data structures are effectively immutable,
// as their operations do not update the structure in-place, but instead
// always yield a new structure.
//
// Persistent
// data structures typically share structure among themselves.  This allows
// operations to avoid copying the entire data structure.
package ps

import . "fmt"

import "bytes"

// Any is a shorthand for Go's verbose interface{} type.
type Any interface{}

// A Map associates unique keys (type string) with values (type Any).
type Map interface {
    // IsNil returns true if the Map is empty
    IsNil() bool

    // Set returns a new map in which key and value are associated.
    // If the key didn't exist before, it's created; otherwise, the
    // associated value is changed.
    // This operation is O(log N) in the number of keys.
    Set(key string, value Any) Map

    // Delete returns a new map with the association for key, if any, removed.
    // This operation is O(log N) in the number of keys.
    Delete(key string) Map

    // Lookup returns the value associated with a key, if any.  If the key
    // exists, the second return value is true; otherwise, false.
    // This operation is O(log N) in the number of keys.
    Lookup(key string) (Any, bool)

    // Size returns the number of key value pairs in the map.
    // This takes O(1) time.
    Size() int

    // ForEach executes a callback on each key value pair in the map.
    ForEach(f func(key string, val Any))

    // Keys returns a slice with all keys in this map.
    // This operation is O(N) in the number of keys.
    Keys() []string

    String() string
}

// Immutable (i.e. persistent) associative array
const childCount = 8
const shiftSize  = 3
type tree struct {
    count   int
    hash    uint64  // hash of the key (used for tree balancing)
    key     string
    value   Any
    children  [childCount]*tree
}
var nilMap = &tree{}

// Recursively set nilMap's subtrees to point at itself.
// This eliminates all nil pointers in the map structure.
// All map nodes are created by cloning this structure so
// they avoid the problem too.
func init () {
    for i := range nilMap.children {
        nilMap.children[i] = nilMap
    }
}

// NewMap allocates a new, persistent map from strings to values of
// any type.
// This is currently implemented as a path-copying binary tree.
func NewMap() Map {
    return nilMap
}

func (self *tree) IsNil() bool {
    return self == nilMap
}

// clone returns an exact duplicate of a tree node
func (self *tree) clone() *tree {
    var m tree
    m = *self
    return &m
}

// constants for FNV-1a hash algorithm
const (
    offset64 uint64 = 14695981039346656037
    prime64  uint64 = 1099511628211
)
// hashKey returns a hash code for a given string
func hashKey(key string) uint64 {
    bytes := []byte(key)
    hash := offset64
    for _, byte := range bytes {
        hash ^= uint64(byte)
        hash *= prime64
    }
    return hash
}

// Set returns a new map similar to this one but with key and value
// associated.  If the key didn't exist, it's created; otherwise, the
// associated value is changed.
func (self *tree) Set(key string, value Any) Map {
    hash := hashKey(key)
    return setLowLevel(self, hash, hash, key, value)
}

func setLowLevel(self *tree, partialHash, hash uint64, key string, value Any) *tree {
    if self.IsNil() { // an empty tree is easy
        m := self.clone()
        m.count = 1
        m.hash  = hash
        m.key   = key
        m.value = value
        return m
    }

    if hash != self.hash {
        m := self.clone()
        i := partialHash % childCount
        m.children[i] = setLowLevel(self.children[i], partialHash>>shiftSize, hash, key, value)
        recalculateCount(m)
        return m
    }

    // replacing a key's previous value
    m := self.clone()
    m.value = value
    return m
}

// modifies a map by recalculating its key count based on the counts
// of its subtrees
func recalculateCount(m *tree) {
    count := 0
    for _, t := range m.children {
        count += t.Size()
    }
    m.count = count + 1 // add one to count ourself
}

func (m *tree) Delete(key string) Map {
    hash := hashKey(key)
    newMap, _ := deleteLowLevel(m, hash, hash)
    return newMap
}

func deleteLowLevel(self *tree, partialHash, hash uint64) (*tree, bool) {
    // empty trees are easy
    if self.IsNil() {
        return self, false
    }

    if hash != self.hash {
        i := partialHash % childCount
        child, found := deleteLowLevel(self.children[i], partialHash>>shiftSize, hash)
        if !found {
            return self, false
        }
        newMap := self.clone()
        newMap.children[i] = child
        recalculateCount(newMap)
        return newMap, true   // ? this wasn't in the original code
    }

    // we must delete our own node
    if self.isLeaf() {  // we have no children
        return nilMap, true
    }
    /*
    if self.subtreeCount() == 1 { // only one subtree
        for _, t := range self.children {
            if t != nilMap {
                return t, true
            }
        }
        panic("Tree with 1 subtree actually had no subtrees")
    }
    */

    // find a node to replace us
    i := -1
    size := -1
    for j, t := range self.children {
        if t.Size() > size {
            i = j
            size = t.Size()
        }
    }

    // make chosen leaf smaller
    replacement, child := self.children[i].deleteLeftmost()
    newMap := replacement.clone()
    for j := range self.children {
        if j == i {
            newMap.children[j] = child
        } else {
            newMap.children[j] = self.children[j]
        }
    }
    recalculateCount(newMap)
    return newMap, true
}

// delete the leftmost node in a tree returning the node that
// was deleted and the tree left over after its deletion
func (m *tree) deleteLeftmost() (*tree, *tree) {
    if m.isLeaf() {
        return m, nilMap
    }

    for i, t := range m.children {
        if t != nilMap {
            deleted, child := t.deleteLeftmost()
            newMap := m.clone()
            newMap.children[i] = child
            recalculateCount(newMap)
            return deleted, newMap
        }
    }
    panic("Tree isn't a leaf but also had no children. How does that happen?")
}

// isLeaf returns true if this is a leaf node
func (m *tree) isLeaf() bool {
    return m.Size() == 1
}

// returns the number of child subtrees we have
func (m *tree) subtreeCount() int {
    count := 0
    for _, t := range m.children {
        if t != nilMap {
            count++
        }
    }
    return count
}

func (m *tree) Lookup(key string) (Any, bool) {
    hash := hashKey(key)
    return lookupLowLevel(m, hash, hash)
}

func lookupLowLevel(self *tree, partialHash, hash uint64) (Any, bool) {
    if self.IsNil() { // an empty tree is easy
        return nil, false
    }

    if hash != self.hash {
        i := partialHash % childCount
        return lookupLowLevel(self.children[i], partialHash>>shiftSize, hash)
    }

    // we found it
    return self.value, true
}

func (m *tree) Size() int {
    return m.count
}

func (m *tree) ForEach(f func(key string, val Any)) {
    if m.IsNil() {
        return
    }

    // ourself
    f(m.key, m.value)

    // children
    for _, t := range m.children {
        if t != nilMap {
            t.ForEach(f)
        }
    }
}

func (m *tree) Keys() []string {
    keys := make([]string, m.Size())
    i := 0
    m.ForEach( func (k string, v Any) {
        keys[i] = k
        i++
    })
    return keys
}

// make it easier to display maps for debugging
func (m *tree) String() string {
    keys := m.Keys()
    buf := bytes.NewBufferString("{")
    for _, key := range keys {
        val, _ := m.Lookup(key)
        Fprintf(buf, "%s: %s, ", key, val)
    }
    Fprintf(buf, "}\n")
    return buf.String()
}
<|endoftext|>


<commit_before>package network

import (
	"testing"
)

func buildNetwork() *Network {
	all_nodes := []*NNode{
		NewNNode(1, InputNeuron),
		NewNNode(2, InputNeuron),
		NewNNode(3, InputNeuron),
		NewNNode(4, HiddenNeuron),
		NewNNode(5, HiddenNeuron),
		NewNNode(6, HiddenNeuron),
		NewNNode(7, OutputNeuron),
		NewNNode(8, OutputNeuron),
	}

	// HIDDEN 4
	all_nodes[3].addIncoming(all_nodes[0], 15.0)
	all_nodes[3].addIncoming(all_nodes[1], 10.0)
	// HIDDEN 5
	all_nodes[4].addIncoming(all_nodes[1], 5.0)
	all_nodes[4].addIncoming(all_nodes[2], 1.0)
	// HIDDEN 6
	all_nodes[5].addIncoming(all_nodes[4], 17.0)
	// OUTPUT 7
	all_nodes[6].addIncoming(all_nodes[3], 7.0)
	all_nodes[6].addIncoming(all_nodes[5], 4.5)
	// OUTPUT 8
	all_nodes[7].addIncoming(all_nodes[5], 13.0)

	return NewNetwork(all_nodes[0:3], all_nodes[6:8], all_nodes, 0)
}

func buildModularNetwork() *Network {
	all_nodes := []*NNode{
		NewNNode(1, InputNeuron),
		NewNNode(2, InputNeuron),
		NewNNode(3, BiasNeuron),
		NewNNode(4, HiddenNeuron),
		NewNNode(5, HiddenNeuron),
		NewNNode(7, HiddenNeuron),
		NewNNode(8, OutputNeuron),
		NewNNode(9, OutputNeuron),
	}
	control_nodes := []*NNode{
		NewNNode(6, HiddenNeuron),
	}
	// HIDDEN 6
	control_nodes[0].ActivationType = MultiplyModuleActivation
	control_nodes[0].addIncoming(all_nodes[3], 17.0)
	control_nodes[0].addIncoming(all_nodes[4], 17.0)
	control_nodes[0].addOutgoing(all_nodes[5], 17.0)

	// HIDDEN 4
	all_nodes[3].ActivationType = LinearActivation
	all_nodes[3].addIncoming(all_nodes[0], 15.0)
	all_nodes[3].addIncoming(all_nodes[2], 10.0)
	// HIDDEN 5
	all_nodes[4].ActivationType = LinearActivation
	all_nodes[4].addIncoming(all_nodes[1], 5.0)
	all_nodes[4].addIncoming(all_nodes[2], 1.0)

	// HIDDEN 7
	all_nodes[5].ActivationType = NullActivation
	all_nodes[5].addIncoming(control_nodes[0], 17.0)

	// OUTPUT 8
	all_nodes[6].addIncoming(all_nodes[5], 4.5)
	all_nodes[6].ActivationType = LinearActivation
	// OUTPUT 9
	all_nodes[7].addIncoming(all_nodes[5], 13.0)
	all_nodes[7].ActivationType = LinearActivation

	return NewModularNetwork(all_nodes[0:2], all_nodes[6:8], all_nodes, control_nodes, 0)
}

func TestModularNetwork_Activate(t *testing.T) {
	netw := buildModularNetwork()
	data := []float64{1.0, 2.0}
	netw.LoadSensors(data)

	depth, err := netw.MaxDepth()
	if err != nil {
		t.Error(err)
	}
	if depth != 5 {
		t.Error("MaxDepth", 5, depth)
	}
	for i := 0; i < depth; i++ {
		res, err := netw.Activate()
		if err != nil {
			t.Error(err)
			return
		}
		if !res {
			t.Error("activation failed unexpectedly")
		}
	}
	if netw.Outputs[0].Activation != 6.750000e+002 {
		t.Error("netw.Outputs[0].Activation != 6.750000e+002", netw.Outputs[0].Activation)
	}
	if netw.Outputs[1].Activation != 1.950000e+003 {
		t.Error("netw.Outputs[1].Activation != 1.950000e+003", netw.Outputs[1].Activation)
	}
}

// Tests Network MaxDepth
func TestNetwork_MaxDepth(t *testing.T) {
	netw := buildNetwork()

	depth, err := netw.MaxDepth()
	if err != nil {
		t.Error(err)
	}
	if depth != 3 {
		t.Error("MaxDepth", 3, depth)
	}
}

// Tests Network OutputIsOff
func TestNetwork_OutputIsOff(t *testing.T) {
	netw := buildNetwork()

	res := netw.OutputIsOff()
	if !res {
		t.Error("OutputIsOff", res)
	}
}

// Tests Network Activate
func TestNetwork_Activate(t *testing.T) {
	netw := buildNetwork()

	res, err := netw.Activate()
	if err != nil {
		t.Error(err)
	}
	if !res {
		t.Error("Failed to activate")
	}
	// check activation
	for _, node := range netw.AllNodes() {
		if node.IsNeuron() {
			if node.ActivationsCount == 0 {
				t.Error("ActivationsCount not set", node.ActivationsCount, node)
			}
			if node.Activation == 0 {
				t.Error("Activation not set", node.Activation, node)
			}
			// Check activation and time delayed activation
			if node.GetActiveOut() == 0 {
				t.Error("GetActiveOut not set", node.GetActiveOut(), node)
			}
		}
	}
}

// Test Network LoadSensors
func TestNetwork_LoadSensors(t *testing.T) {
	netw := buildNetwork()

	sensors := []float64{1.0, 3.4, 5.6}

	netw.LoadSensors(sensors)
	counter := 0
	for _, node := range netw.AllNodes() {
		if node.IsSensor() {
			if node.Activation != sensors[counter] {
				t.Error("Sensor value wrong", sensors[counter], node.Activation)
			}
			if node.ActivationsCount != 1 {
				t.Error("Sensor activations count wrong", 1, node.ActivationsCount)
			}
			counter++
		}
	}
}

// Test Network Flush
func TestNetwork_Flush(t *testing.T) {
	netw := buildNetwork()

	// activate and check state
	res, err := netw.Activate()
	if err != nil {
		t.Error(err)
	}
	if !res {
		t.Error("Failed to activate")
	}
	netw.Activate()

	// flush and check
	res, err = netw.Flush()
	if err != nil {
		t.Error(err)
		return
	}
	if !res {
		t.Error("Network flush failed")
		return
	}

	for _, node := range netw.AllNodes() {
		if node.ActivationsCount != 0 {
			t.Error("ActivationsCount", 0, node.ActivationsCount)
		}
		if node.Activation != 0 {
			t.Error("Activation", 0, node.Activation)
		}
		// Check activation and time delayed activation
		if node.GetActiveOut() != 0 {
			t.Error("GetActiveOut", 0, node.GetActiveOut())
		}
		if node.GetActiveOutTd() != 0 {
			t.Error("GetActiveOutTd", 0, node.GetActiveOutTd())
		}
	}
}

// Tests Network NodeCount
func TestNetwork_NodeCount(t *testing.T) {
	netw := buildNetwork()

	count := netw.NodeCount()
	if count != 8 {
		t.Error("Wrong network's node count", 8, count)
	}
}

// Tests Network LinkCount
func TestNetwork_LinkCount(t *testing.T) {
	netw := buildNetwork()

	count := netw.LinkCount()
	if count != 8 {
		t.Error("Wrong network's link count", 8, count)
	}
}

// Tests Network IsRecurrent
func TestNetwork_IsRecurrent(t *testing.T) {
	netw := buildNetwork()

	nodes := netw.AllNodes()

	count := 0
	recur := netw.IsRecurrent(nodes[0], nodes[7], &count, 32)
	if recur {
		t.Error("Network is not recurrent")
	}

	// Introduce recurrence
	nodes[4].addIncoming(nodes[7], 3.0)

	recur = netw.IsRecurrent(nodes[5], nodes[7], &count, 32)
	if !recur {
		t.Error("Network is actually recurrent now")
	}
}

// test fast network solver generation
func TestNetwork_FastNetworkSolver(t *testing.T) {
	netw := buildModularNetwork()

	solver, err := netw.FastNetworkSolver()
	if err != nil {
		t.Error(err)
		return
	}

	// check solver
	if solver.NodeCount() != netw.NodeCount() {
		t.Error("solver.NodeCount() != netw.NodeCount()", solver.NodeCount(), netw.NodeCount())
	}
	if solver.LinkCount() != netw.LinkCount() {
		t.Error("solver.LinkCount() != netw.LinkCount()", solver.LinkCount(), netw.LinkCount())
	}
}
<commit_msg>Fixed modular network builder by removing duplicate link definition for control node. Removed depth checking for modular network - unsupported yet.<commit_after>package network

import (
	"testing"
)

func buildNetwork() *Network {
	all_nodes := []*NNode{
		NewNNode(1, InputNeuron),
		NewNNode(2, InputNeuron),
		NewNNode(3, InputNeuron),
		NewNNode(4, HiddenNeuron),
		NewNNode(5, HiddenNeuron),
		NewNNode(6, HiddenNeuron),
		NewNNode(7, OutputNeuron),
		NewNNode(8, OutputNeuron),
	}

	// HIDDEN 4
	all_nodes[3].addIncoming(all_nodes[0], 15.0)
	all_nodes[3].addIncoming(all_nodes[1], 10.0)
	// HIDDEN 5
	all_nodes[4].addIncoming(all_nodes[1], 5.0)
	all_nodes[4].addIncoming(all_nodes[2], 1.0)
	// HIDDEN 6
	all_nodes[5].addIncoming(all_nodes[4], 17.0)
	// OUTPUT 7
	all_nodes[6].addIncoming(all_nodes[3], 7.0)
	all_nodes[6].addIncoming(all_nodes[5], 4.5)
	// OUTPUT 8
	all_nodes[7].addIncoming(all_nodes[5], 13.0)

	return NewNetwork(all_nodes[0:3], all_nodes[6:8], all_nodes, 0)
}

func buildModularNetwork() *Network {
	all_nodes := []*NNode{
		NewNNode(1, InputNeuron),
		NewNNode(2, InputNeuron),
		NewNNode(3, BiasNeuron),
		NewNNode(4, HiddenNeuron),
		NewNNode(5, HiddenNeuron),
		NewNNode(7, HiddenNeuron),
		NewNNode(8, OutputNeuron),
		NewNNode(9, OutputNeuron),
	}
	control_nodes := []*NNode{
		NewNNode(6, HiddenNeuron),
	}
	// HIDDEN 6
	control_nodes[0].ActivationType = MultiplyModuleActivation
	control_nodes[0].addIncoming(all_nodes[3], 17.0)
	control_nodes[0].addIncoming(all_nodes[4], 17.0)
	control_nodes[0].addOutgoing(all_nodes[5], 17.0)

	// HIDDEN 4
	all_nodes[3].ActivationType = LinearActivation
	all_nodes[3].addIncoming(all_nodes[0], 15.0)
	all_nodes[3].addIncoming(all_nodes[2], 10.0)
	// HIDDEN 5
	all_nodes[4].ActivationType = LinearActivation
	all_nodes[4].addIncoming(all_nodes[1], 5.0)
	all_nodes[4].addIncoming(all_nodes[2], 1.0)

	// HIDDEN 7
	all_nodes[5].ActivationType = NullActivation

	// OUTPUT 8
	all_nodes[6].addIncoming(all_nodes[5], 4.5)
	all_nodes[6].ActivationType = LinearActivation
	// OUTPUT 9
	all_nodes[7].addIncoming(all_nodes[5], 13.0)
	all_nodes[7].ActivationType = LinearActivation

	return NewModularNetwork(all_nodes[0:2], all_nodes[6:8], all_nodes, control_nodes, 0)
}

func TestModularNetwork_Activate(t *testing.T) {
	netw := buildModularNetwork()
	data := []float64{1.0, 2.0}
	netw.LoadSensors(data)

	for i := 0; i < 5; i++ {
		res, err := netw.Activate()
		if err != nil {
			t.Error(err)
			return
		}
		if !res {
			t.Error("activation failed unexpectedly")
		}
	}
	if netw.Outputs[0].Activation != 6.750000e+002 {
		t.Error("netw.Outputs[0].Activation != 6.750000e+002", netw.Outputs[0].Activation)
	}
	if netw.Outputs[1].Activation != 1.950000e+003 {
		t.Error("netw.Outputs[1].Activation != 1.950000e+003", netw.Outputs[1].Activation)
	}
}

// Tests Network MaxDepth
func TestNetwork_MaxDepth(t *testing.T) {
	netw := buildNetwork()

	depth, err := netw.MaxDepth()
	if err != nil {
		t.Error(err)
	}
	if depth != 3 {
		t.Error("MaxDepth", 3, depth)
	}
}

// Tests Network OutputIsOff
func TestNetwork_OutputIsOff(t *testing.T) {
	netw := buildNetwork()

	res := netw.OutputIsOff()
	if !res {
		t.Error("OutputIsOff", res)
	}
}

// Tests Network Activate
func TestNetwork_Activate(t *testing.T) {
	netw := buildNetwork()

	res, err := netw.Activate()
	if err != nil {
		t.Error(err)
	}
	if !res {
		t.Error("Failed to activate")
	}
	// check activation
	for _, node := range netw.AllNodes() {
		if node.IsNeuron() {
			if node.ActivationsCount == 0 {
				t.Error("ActivationsCount not set", node.ActivationsCount, node)
			}
			if node.Activation == 0 {
				t.Error("Activation not set", node.Activation, node)
			}
			// Check activation and time delayed activation
			if node.GetActiveOut() == 0 {
				t.Error("GetActiveOut not set", node.GetActiveOut(), node)
			}
		}
	}
}

// Test Network LoadSensors
func TestNetwork_LoadSensors(t *testing.T) {
	netw := buildNetwork()

	sensors := []float64{1.0, 3.4, 5.6}

	netw.LoadSensors(sensors)
	counter := 0
	for _, node := range netw.AllNodes() {
		if node.IsSensor() {
			if node.Activation != sensors[counter] {
				t.Error("Sensor value wrong", sensors[counter], node.Activation)
			}
			if node.ActivationsCount != 1 {
				t.Error("Sensor activations count wrong", 1, node.ActivationsCount)
			}
			counter++
		}
	}
}

// Test Network Flush
func TestNetwork_Flush(t *testing.T) {
	netw := buildNetwork()

	// activate and check state
	res, err := netw.Activate()
	if err != nil {
		t.Error(err)
	}
	if !res {
		t.Error("Failed to activate")
	}
	netw.Activate()

	// flush and check
	res, err = netw.Flush()
	if err != nil {
		t.Error(err)
		return
	}
	if !res {
		t.Error("Network flush failed")
		return
	}

	for _, node := range netw.AllNodes() {
		if node.ActivationsCount != 0 {
			t.Error("ActivationsCount", 0, node.ActivationsCount)
		}
		if node.Activation != 0 {
			t.Error("Activation", 0, node.Activation)
		}
		// Check activation and time delayed activation
		if node.GetActiveOut() != 0 {
			t.Error("GetActiveOut", 0, node.GetActiveOut())
		}
		if node.GetActiveOutTd() != 0 {
			t.Error("GetActiveOutTd", 0, node.GetActiveOutTd())
		}
	}
}

// Tests Network NodeCount
func TestNetwork_NodeCount(t *testing.T) {
	netw := buildNetwork()

	count := netw.NodeCount()
	if count != 8 {
		t.Error("Wrong network's node count", 8, count)
	}
}

// Tests Network LinkCount
func TestNetwork_LinkCount(t *testing.T) {
	netw := buildNetwork()

	count := netw.LinkCount()
	if count != 8 {
		t.Error("Wrong network's link count", 8, count)
	}
}

// Tests Network IsRecurrent
func TestNetwork_IsRecurrent(t *testing.T) {
	netw := buildNetwork()

	nodes := netw.AllNodes()

	count := 0
	recur := netw.IsRecurrent(nodes[0], nodes[7], &count, 32)
	if recur {
		t.Error("Network is not recurrent")
	}

	// Introduce recurrence
	nodes[4].addIncoming(nodes[7], 3.0)

	recur = netw.IsRecurrent(nodes[5], nodes[7], &count, 32)
	if !recur {
		t.Error("Network is actually recurrent now")
	}
}

// test fast network solver generation
func TestNetwork_FastNetworkSolver(t *testing.T) {
	netw := buildModularNetwork()

	solver, err := netw.FastNetworkSolver()
	if err != nil {
		t.Error(err)
		return
	}

	// check solver
	if solver.NodeCount() != netw.NodeCount() {
		t.Error("solver.NodeCount() != netw.NodeCount()", solver.NodeCount(), netw.NodeCount())
	}
	if solver.LinkCount() != netw.LinkCount() {
		t.Error("solver.LinkCount() != netw.LinkCount()", solver.LinkCount(), netw.LinkCount())
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/u-root/u-root/pkg/cpio"
	"github.com/u-root/u-root/pkg/testutil"
	itest "github.com/u-root/u-root/pkg/uroot/initramfs/test"
)

var twocmds = []string{
	"github.com/u-root/u-root/cmds/core/ls",
	"github.com/u-root/u-root/cmds/core/init",
}

var srcmds = []string{
	"github.com/u-root/u-root/cmds/core/ls",
	"github.com/u-root/u-root/cmds/core/init",
	"github.com/u-root/u-root/cmds/core/installcommand",
}

type buildSourceValidator struct {
	gopath string
	goroot string
	env    []string
}

func (b buildSourceValidator) Validate(a *cpio.Archive) error {
	dir, err := ioutil.TempDir("", "u-root-source-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(dir)

	if err := os.Mkdir(filepath.Join(dir, "tmp"), 0755); err != nil {
		return err
	}

	// Unpack into dir.
	err = cpio.ForEachRecord(a.Reader(), func(r cpio.Record) error {
		return cpio.CreateFileInRoot(r, dir, false)
	})
	if err != nil {
		return err
	}

	goroot := filepath.Join(dir, b.goroot)
	gopath := filepath.Join(dir, b.gopath)
	// go build ./src/...
	c := exec.Command(filepath.Join(goroot, "bin/go"), "build", filepath.Join(gopath, "src/..."))
	c.Env = append(b.env,
		fmt.Sprintf("GOPATH=%s", gopath),
		fmt.Sprintf("GOCACHE=%s", filepath.Join(dir, "tmp")),
		fmt.Sprintf("GOROOT=%s", goroot),
		"CGO_ENABLED=0")
	out, err := c.CombinedOutput()
	if err != nil {
		return fmt.Errorf("could not build go source %v; output\n%s", err, out)
	}
	return nil
}

func TestUrootCmdline(t *testing.T) {
	samplef, err := ioutil.TempFile("", "u-root-sample-")
	if err != nil {
		t.Fatal(err)
	}
	samplef.Close()
	defer os.RemoveAll(samplef.Name())

	for _, tt := range []struct {
		name       string
		env        []string
		args       []string
		err        error
		validators []itest.ArchiveValidator
	}{
		{
			name: "include one extra file",
			args: []string{"-nocmd", "-files=/bin/bash"},
			err:  nil,
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bash"},
			},
		},
		{
			name: "uinitcmd",
			args: []string{"-build=bb", "-uinitcmd=echo foobar fuzz", "-defaultsh=", "./cmds/core/init", "./cmds/core/echo"},
			err:  nil,
			validators: []itest.ArchiveValidator{
				itest.HasRecord{cpio.Symlink("bin/uinit", "../bbin/echo")},
				itest.HasContent{
					Path:    "etc/uinit.flags",
					Content: "\"foobar\"\n\"fuzz\"",
				},
			},
		},
		{
			name: "fix usage of an absolute path",
			args: []string{"-nocmd", "-files=/bin:/bin"},
			err:  nil,
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bash"},
			},
		},
		{
			name: "include multiple extra files",
			args: []string{"-nocmd", "-files=/bin/bash", "-files=/bin/ls", fmt.Sprintf("-files=%s", samplef.Name())},
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bash"},
				itest.HasFile{"bin/ls"},
				itest.HasFile{samplef.Name()},
			},
		},
		{
			name: "include one extra file with rename",
			args: []string{"-nocmd", "-files=/bin/bash:bin/bush"},
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bush"},
			},
		},
		{
			name: "hosted source mode",
			args: append([]string{"-build=source", "-base=/dev/null", "-defaultsh=", "-initcmd="}, srcmds...),
		},
		{
			name: "hosted bb mode",
			args: append([]string{"-build=bb", "-base=/dev/null", "-defaultsh=", "-initcmd="}, twocmds...),
		},
		{
			name: "AMD64 bb build",
			env:  []string{"GOARCH=amd64"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "AMD64 source build",
			env:  []string{"GOARCH=amd64"},
			args: []string{"-build=source", "all"},
			validators: []itest.ArchiveValidator{
				buildSourceValidator{
					goroot: "/go",
					gopath: ".",
					env:    []string{"GOARCH=amd64"},
				},
			},
		},
		{
			name: "MIPS bb build",
			env:  []string{"GOARCH=mips"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "MIPSLE bb build",
			env:  []string{"GOARCH=mipsle"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "MIPS64 bb build",
			env:  []string{"GOARCH=mips64"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "MIPS64LE bb build",
			env:  []string{"GOARCH=mips64le"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "ARM7 bb build",
			env:  []string{"GOARCH=arm", "GOARM=7"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "ARM64 bb build",
			env:  []string{"GOARCH=arm64"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "386 (32 bit) bb build",
			env:  []string{"GOARCH=386"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "Power 64bit bb build",
			env:  []string{"GOARCH=ppc64le"},
			args: []string{"-build=bb", "all"},
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			delFiles := true
			f, sum1 := buildIt(t, tt.args, tt.env, tt.err)
			defer func() {
				if delFiles {
					os.RemoveAll(f.Name())
				}
			}()

			a, err := itest.ReadArchive(f.Name())
			if err != nil {
				t.Fatal(err)
			}

			for _, v := range tt.validators {
				if err := v.Validate(a); err != nil {
					t.Errorf("validator failed: %v / archive:\n%s", err, a)
				}
			}

			f2, sum2 := buildIt(t, tt.args, tt.env, tt.err)
			defer func() {
				if delFiles {
					os.RemoveAll(f2.Name())
				}
			}()
			if !bytes.Equal(sum1, sum2) {
				delFiles = false
				t.Errorf("not reproducible, hashes don't match")
				t.Errorf("env: %v args: %v", tt.env, tt.args)
				t.Errorf("file1: %v file2: %v", f.Name(), f2.Name())
			}
		})
	}
}

func buildIt(t *testing.T, args, env []string, want error) (*os.File, []byte) {
	f, err := ioutil.TempFile("", "u-root-")
	if err != nil {
		t.Fatal(err)
	}

	arg := append([]string{"-o", f.Name()}, args...)
	c := testutil.Command(t, arg...)
	t.Logf("Commandline: %v", arg)
	c.Env = append(c.Env, env...)
	if out, err := c.CombinedOutput(); err != want {
		t.Fatalf("Error: %v\nOutput:\n%s", err, out)
	} else if err != nil {
		h1 := sha256.New()
		if _, err := io.Copy(h1, f); err != nil {
			t.Fatal()
		}
		return f, h1.Sum(nil)
	}
	return f, nil
}

func TestMain(m *testing.M) {
	testutil.Run(m, main)
}
<commit_msg>Fix absolute path usage test<commit_after>// Copyright 2015-2018 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"testing"

	"github.com/u-root/u-root/pkg/cpio"
	"github.com/u-root/u-root/pkg/testutil"
	itest "github.com/u-root/u-root/pkg/uroot/initramfs/test"
)

var twocmds = []string{
	"github.com/u-root/u-root/cmds/core/ls",
	"github.com/u-root/u-root/cmds/core/init",
}

var srcmds = []string{
	"github.com/u-root/u-root/cmds/core/ls",
	"github.com/u-root/u-root/cmds/core/init",
	"github.com/u-root/u-root/cmds/core/installcommand",
}

type buildSourceValidator struct {
	gopath string
	goroot string
	env    []string
}

func (b buildSourceValidator) Validate(a *cpio.Archive) error {
	dir, err := ioutil.TempDir("", "u-root-source-")
	if err != nil {
		return err
	}
	defer os.RemoveAll(dir)

	if err := os.Mkdir(filepath.Join(dir, "tmp"), 0755); err != nil {
		return err
	}

	// Unpack into dir.
	err = cpio.ForEachRecord(a.Reader(), func(r cpio.Record) error {
		return cpio.CreateFileInRoot(r, dir, false)
	})
	if err != nil {
		return err
	}

	goroot := filepath.Join(dir, b.goroot)
	gopath := filepath.Join(dir, b.gopath)
	// go build ./src/...
	c := exec.Command(filepath.Join(goroot, "bin/go"), "build", filepath.Join(gopath, "src/..."))
	c.Env = append(b.env,
		fmt.Sprintf("GOPATH=%s", gopath),
		fmt.Sprintf("GOCACHE=%s", filepath.Join(dir, "tmp")),
		fmt.Sprintf("GOROOT=%s", goroot),
		"CGO_ENABLED=0")
	out, err := c.CombinedOutput()
	if err != nil {
		return fmt.Errorf("could not build go source %v; output\n%s", err, out)
	}
	return nil
}

func TestUrootCmdline(t *testing.T) {
	samplef, err := ioutil.TempFile("", "u-root-test-")
	if err != nil {
		t.Fatal(err)
	}
	samplef.Close()
	defer os.RemoveAll(samplef.Name())
	sampledir, err := ioutil.TempDir("", "u-root-test-dir-")
	if err != nil {
		t.Fatal(err)
	}
	if err = ioutil.WriteFile(filepath.Join(sampledir, "foo"), nil, 0644); err != nil {
		t.Fatal(err)
	}
	if err = ioutil.WriteFile(filepath.Join(sampledir, "bar"), nil, 0644); err != nil {
		t.Fatal(err)
	}
	defer os.RemoveAll(sampledir)

	for _, tt := range []struct {
		name       string
		env        []string
		args       []string
		err        error
		validators []itest.ArchiveValidator
	}{
		{
			name: "include one extra file",
			args: []string{"-nocmd", "-files=/bin/bash"},
			err:  nil,
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bash"},
			},
		},
		{
			name: "uinitcmd",
			args: []string{"-build=bb", "-uinitcmd=echo foobar fuzz", "-defaultsh=", "./cmds/core/init", "./cmds/core/echo"},
			err:  nil,
			validators: []itest.ArchiveValidator{
				itest.HasRecord{cpio.Symlink("bin/uinit", "../bbin/echo")},
				itest.HasContent{
					Path:    "etc/uinit.flags",
					Content: "\"foobar\"\n\"fuzz\"",
				},
			},
		},
		{
			name: "fix usage of an absolute path",
			args: []string{"-nocmd", fmt.Sprintf("-files=%s:/bin", sampledir)},
			err:  nil,
			validators: []itest.ArchiveValidator{
				itest.HasFile{"/bin/foo"},
				itest.HasFile{"/bin/bar"},
			},
		},
		{
			name: "include multiple extra files",
			args: []string{"-nocmd", "-files=/bin/bash", "-files=/bin/ls", fmt.Sprintf("-files=%s", samplef.Name())},
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bash"},
				itest.HasFile{"bin/ls"},
				itest.HasFile{samplef.Name()},
			},
		},
		{
			name: "include one extra file with rename",
			args: []string{"-nocmd", "-files=/bin/bash:bin/bush"},
			validators: []itest.ArchiveValidator{
				itest.HasFile{"bin/bush"},
			},
		},
		{
			name: "hosted source mode",
			args: append([]string{"-build=source", "-base=/dev/null", "-defaultsh=", "-initcmd="}, srcmds...),
		},
		{
			name: "hosted bb mode",
			args: append([]string{"-build=bb", "-base=/dev/null", "-defaultsh=", "-initcmd="}, twocmds...),
		},
		{
			name: "AMD64 bb build",
			env:  []string{"GOARCH=amd64"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "AMD64 source build",
			env:  []string{"GOARCH=amd64"},
			args: []string{"-build=source", "all"},
			validators: []itest.ArchiveValidator{
				buildSourceValidator{
					goroot: "/go",
					gopath: ".",
					env:    []string{"GOARCH=amd64"},
				},
			},
		},
		{
			name: "MIPS bb build",
			env:  []string{"GOARCH=mips"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "MIPSLE bb build",
			env:  []string{"GOARCH=mipsle"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "MIPS64 bb build",
			env:  []string{"GOARCH=mips64"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "MIPS64LE bb build",
			env:  []string{"GOARCH=mips64le"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "ARM7 bb build",
			env:  []string{"GOARCH=arm", "GOARM=7"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "ARM64 bb build",
			env:  []string{"GOARCH=arm64"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "386 (32 bit) bb build",
			env:  []string{"GOARCH=386"},
			args: []string{"-build=bb", "all"},
		},
		{
			name: "Power 64bit bb build",
			env:  []string{"GOARCH=ppc64le"},
			args: []string{"-build=bb", "all"},
		},
	} {
		t.Run(tt.name, func(t *testing.T) {
			delFiles := true
			f, sum1 := buildIt(t, tt.args, tt.env, tt.err)
			defer func() {
				if delFiles {
					os.RemoveAll(f.Name())
				}
			}()

			a, err := itest.ReadArchive(f.Name())
			if err != nil {
				t.Fatal(err)
			}

			for _, v := range tt.validators {
				if err := v.Validate(a); err != nil {
					t.Errorf("validator failed: %v / archive:\n%s", err, a)
				}
			}

			f2, sum2 := buildIt(t, tt.args, tt.env, tt.err)
			defer func() {
				if delFiles {
					os.RemoveAll(f2.Name())
				}
			}()
			if !bytes.Equal(sum1, sum2) {
				delFiles = false
				t.Errorf("not reproducible, hashes don't match")
				t.Errorf("env: %v args: %v", tt.env, tt.args)
				t.Errorf("file1: %v file2: %v", f.Name(), f2.Name())
			}
		})
	}
}

func buildIt(t *testing.T, args, env []string, want error) (*os.File, []byte) {
	f, err := ioutil.TempFile("", "u-root-")
	if err != nil {
		t.Fatal(err)
	}

	arg := append([]string{"-o", f.Name()}, args...)
	c := testutil.Command(t, arg...)
	t.Logf("Commandline: %v", arg)
	c.Env = append(c.Env, env...)
	if out, err := c.CombinedOutput(); err != want {
		t.Fatalf("Error: %v\nOutput:\n%s", err, out)
	} else if err != nil {
		h1 := sha256.New()
		if _, err := io.Copy(h1, f); err != nil {
			t.Fatal()
		}
		return f, h1.Sum(nil)
	}
	return f, nil
}

func TestMain(m *testing.M) {
	testutil.Run(m, main)
}
<|endoftext|>


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

package cl_test

import (
	"bytes"
	"os"
	"path"
	"testing"

	"github.com/goplus/gop/cl"
	"github.com/goplus/gop/parser"
	"github.com/goplus/gop/parser/parsertest"
	"github.com/goplus/gop/scanner"
	"github.com/goplus/gox"
)

func newTwoFileFS(dir string, fname, data string, fname2 string, data2 string) *parsertest.MemFS {
	return parsertest.NewMemFS(map[string][]string{
		dir: {fname, fname2},
	}, map[string]string{
		path.Join(dir, fname):  data,
		path.Join(dir, fname2): data2,
	})
}

func init() {
	cl.RegisterClassFileType(".tgmx", ".tspx", "github.com/goplus/gop/cl/internal/spx", "math")
}

func gopSpxTest(t *testing.T, gmx, spxcode, expected string) {
	gopSpxTestEx(t, gmx, spxcode, expected, "index.tgmx", "bar.tspx")
}

func gopSpxTestEx(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile string) {
	cl.SetDisableRecover(true)
	defer cl.SetDisableRecover(false)

	fs := newTwoFileFS("/foo", spxfile, spxcode, gmxfile, gmx)
	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", nil, 0)
	if err != nil {
		scanner.PrintError(os.Stderr, err)
		t.Fatal("ParseFSDir:", err)
	}
	conf := *baseConf.Ensure()
	bar := pkgs["main"]
	pkg, err := cl.NewPackage("", bar, &conf)
	if err != nil {
		t.Fatal("NewPackage:", err)
	}
	var b bytes.Buffer
	err = gox.WriteTo(&b, pkg, false)
	if err != nil {
		t.Fatal("gox.WriteTo failed:", err)
	}
	result := b.String()
	if result != expected {
		t.Fatalf("\nResult:\n%s\nExpected:\n%s\n", result, expected)
	}
}

func TestSpxBasic(t *testing.T) {
	gopSpxTestEx(t, `
func onInit() {
	for {
	}
}
`, `
func onMsg(msg string) {
	for {
		say "Hi"
	}
}
`, `package main

import spx "github.com/goplus/gop/cl/internal/spx"

type Game struct {
	*spx.MyGame
}

func (this *Game) onInit() {
	for {
		spx.SchedNow()
	}
}

type Kai struct {
	spx.Sprite
	*Game
}

func (this *Kai) onMsg(msg string) {
	for {
		spx.Sched()
		this.Say("Hi")
	}
}
`, "Game.tgmx", "Kai.tspx")
}

func TestSpxBasic2(t *testing.T) {
	gopSpxTest(t, `
import (
	"fmt"
)

const (
	Foo = 1
)

func bar() {
}

func onInit() {
	Foo
	bar
	fmt.Println("Hi")
}
`, ``, `package main

import (
	fmt "fmt"
	spx "github.com/goplus/gop/cl/internal/spx"
)

const Foo = 1

type index struct {
	*spx.MyGame
}

func (this *index) bar() {
}
func (this *index) onInit() {
	Foo
	this.bar()
	fmt.Println("Hi")
}
`)
}

func TestSpxMethod(t *testing.T) {
	gopSpxTestEx(t, `
func onInit() {
	sched
	broadcast "msg1"
	testIntValue = 1
	x := round(1.2)
}
`, `
func onInit() {
	setCostume "kai-a"
	play "recordingWhere"
	say "Where do you come from?", 2
	broadcast "msg2"
}
`, `package main

import (
	spx "github.com/goplus/gop/cl/internal/spx"
	math "math"
)

type Game struct {
	*spx.MyGame
}

func (this *Game) onInit() {
	spx.Sched()
	this.Broadcast__0("msg1")
	spx.TestIntValue = 1
	x := math.Round(1.2)
}

type bar struct {
	spx.Sprite
	*Game
}

func (this *bar) onInit() {
	this.SetCostume("kai-a")
	this.Play("recordingWhere")
	this.Say("Where do you come from?", 2)
	this.Broadcast__0("msg2")
}
`, "Game.tgmx", "bar.tspx")
}

func TestSpxVar(t *testing.T) {
	gopSpxTestEx(t, `
var (
	Kai Kai
)

func onInit() {
	Kai.clone()
	broadcast("msg1")
}
`, `
var (
	a int
)

func onInit() {
	a = 1
}

func onCloned() {
	say("Hi")
}
`, `package main

import spx "github.com/goplus/gop/cl/internal/spx"

type Game struct {
	*spx.MyGame
	Kai Kai
}
type Kai struct {
	spx.Sprite
	*Game
	a int
}

func (this *Game) onInit() {
	this.Kai.Clone()
	this.Broadcast__0("msg1")
}
func (this *Kai) onInit() {
	this.a = 1
}
func (this *Kai) onCloned() {
	this.Say("Hi")
}
`, "Game.tgmx", "Kai.tspx")
}

func TestSpxRun(t *testing.T) {
	gopSpxTestEx(t, `
var (
	Kai Kai
	t   Sound
)

run "hzip://open.qiniu.us/weather/res.zip"
`, `
println "Hi"
`, `package main

import (
	fmt "fmt"
	spx "github.com/goplus/gop/cl/internal/spx"
)

type index struct {
	*spx.MyGame
	Kai Kai
	t   spx.Sound
}
type Kai struct {
	spx.Sprite
	*index
}

func (this *index) MainEntry() {
	spx.Gopt_MyGame_Run(this, "hzip://open.qiniu.us/weather/res.zip")
}
func main() {
	spx.Gopt_MyGame_Main(new(index))
}
func (this *Kai) Main() {
	fmt.Println("Hi")
}
`, "index.tgmx", "Kai.tspx")
}

func TestSpx2(t *testing.T) {
	gopSpxTestEx(t, `
println("Hi")
`, `
func onMsg(msg string) {
}
`, `package main

import (
	fmt "fmt"
	spx2 "github.com/goplus/gop/cl/internal/spx2"
)

type Game struct {
	spx2.Game
}

func (this *Game) MainEntry() {
	fmt.Println("Hi")
}
func main() {
	new(Game).Main()
}

type Kai struct {
	spx2.Sprite
	*Game
}

func (this *Kai) onMsg(msg string) {
}
`, "Game.t2gmx", "Kai.t2spx")
}
<commit_msg>cl: add spx error test<commit_after>/*
 * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cl_test

import (
	"bytes"
	"os"
	"path"
	"testing"

	"github.com/goplus/gop/cl"
	"github.com/goplus/gop/parser"
	"github.com/goplus/gop/parser/parsertest"
	"github.com/goplus/gop/scanner"
	"github.com/goplus/gox"
)

func newTwoFileFS(dir string, fname, data string, fname2 string, data2 string) *parsertest.MemFS {
	return parsertest.NewMemFS(map[string][]string{
		dir: {fname, fname2},
	}, map[string]string{
		path.Join(dir, fname):  data,
		path.Join(dir, fname2): data2,
	})
}

func init() {
	cl.RegisterClassFileType(".tgmx", ".tspx", "github.com/goplus/gop/cl/internal/spx", "math")
}

func gopSpxTest(t *testing.T, gmx, spxcode, expected string) {
	gopSpxTestEx(t, gmx, spxcode, expected, "index.tgmx", "bar.tspx")
}

func gopSpxTestEx(t *testing.T, gmx, spxcode, expected, gmxfile, spxfile string) {
	cl.SetDisableRecover(true)
	defer cl.SetDisableRecover(false)

	fs := newTwoFileFS("/foo", spxfile, spxcode, gmxfile, gmx)
	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", nil, 0)
	if err != nil {
		scanner.PrintError(os.Stderr, err)
		t.Fatal("ParseFSDir:", err)
	}
	conf := *baseConf.Ensure()
	bar := pkgs["main"]
	pkg, err := cl.NewPackage("", bar, &conf)
	if err != nil {
		t.Fatal("NewPackage:", err)
	}
	var b bytes.Buffer
	err = gox.WriteTo(&b, pkg, false)
	if err != nil {
		t.Fatal("gox.WriteTo failed:", err)
	}
	result := b.String()
	if result != expected {
		t.Fatalf("\nResult:\n%s\nExpected:\n%s\n", result, expected)
	}
}

func gopSpxErrorTestEx(t *testing.T, msg, gmx, spxcode, gmxfile, spxfile string) {
	fs := newTwoFileFS("/foo", spxfile, spxcode, gmxfile, gmx)
	pkgs, err := parser.ParseFSDir(gblFset, fs, "/foo", nil, 0)
	if err != nil {
		scanner.PrintError(os.Stderr, err)
		t.Fatal("ParseFSDir:", err)
	}
	conf := *baseConf.Ensure()
	conf.NoFileLine = false
	conf.WorkingDir = "/foo"
	conf.TargetDir = "/foo"
	bar := pkgs["main"]
	_, err = cl.NewPackage("", bar, &conf)
	if err == nil {
		t.Fatal("no error?")
	}
	if ret := err.Error(); ret != msg {
		t.Fatalf("\nError: \"%s\"\nExpected: \"%s\"\n", ret, msg)
	}
}

func TestSpxError(t *testing.T) {
	gopSpxErrorTestEx(t, `./Game.tgmx:4:2: cannot assign value to field in class file`, `
var (
	Kai Kai
	userScore int = 100
)
`, `
println "hi"
`, "Game.tgmx", "Kai.tspx")

	gopSpxErrorTestEx(t, `./Kai.tspx:3:2: missing field type in class file`, `
var (
	Kai Kai
	userScore int
)
`, `
var (
	id = 100
)
println "hi"
`, "Game.tgmx", "Kai.tspx")
}

func TestSpxBasic(t *testing.T) {
	gopSpxTestEx(t, `
func onInit() {
	for {
	}
}
`, `
func onMsg(msg string) {
	for {
		say "Hi"
	}
}
`, `package main

import spx "github.com/goplus/gop/cl/internal/spx"

type Game struct {
	*spx.MyGame
}

func (this *Game) onInit() {
	for {
		spx.SchedNow()
	}
}

type Kai struct {
	spx.Sprite
	*Game
}

func (this *Kai) onMsg(msg string) {
	for {
		spx.Sched()
		this.Say("Hi")
	}
}
`, "Game.tgmx", "Kai.tspx")
}

func TestSpxBasic2(t *testing.T) {
	gopSpxTest(t, `
import (
	"fmt"
)

const (
	Foo = 1
)

func bar() {
}

func onInit() {
	Foo
	bar
	fmt.Println("Hi")
}
`, ``, `package main

import (
	fmt "fmt"
	spx "github.com/goplus/gop/cl/internal/spx"
)

const Foo = 1

type index struct {
	*spx.MyGame
}

func (this *index) bar() {
}
func (this *index) onInit() {
	Foo
	this.bar()
	fmt.Println("Hi")
}
`)
}

func TestSpxMethod(t *testing.T) {
	gopSpxTestEx(t, `
func onInit() {
	sched
	broadcast "msg1"
	testIntValue = 1
	x := round(1.2)
}
`, `
func onInit() {
	setCostume "kai-a"
	play "recordingWhere"
	say "Where do you come from?", 2
	broadcast "msg2"
}
`, `package main

import (
	spx "github.com/goplus/gop/cl/internal/spx"
	math "math"
)

type Game struct {
	*spx.MyGame
}

func (this *Game) onInit() {
	spx.Sched()
	this.Broadcast__0("msg1")
	spx.TestIntValue = 1
	x := math.Round(1.2)
}

type bar struct {
	spx.Sprite
	*Game
}

func (this *bar) onInit() {
	this.SetCostume("kai-a")
	this.Play("recordingWhere")
	this.Say("Where do you come from?", 2)
	this.Broadcast__0("msg2")
}
`, "Game.tgmx", "bar.tspx")
}

func TestSpxVar(t *testing.T) {
	gopSpxTestEx(t, `
var (
	Kai Kai
)

func onInit() {
	Kai.clone()
	broadcast("msg1")
}
`, `
var (
	a int
)

func onInit() {
	a = 1
}

func onCloned() {
	say("Hi")
}
`, `package main

import spx "github.com/goplus/gop/cl/internal/spx"

type Game struct {
	*spx.MyGame
	Kai Kai
}
type Kai struct {
	spx.Sprite
	*Game
	a int
}

func (this *Game) onInit() {
	this.Kai.Clone()
	this.Broadcast__0("msg1")
}
func (this *Kai) onInit() {
	this.a = 1
}
func (this *Kai) onCloned() {
	this.Say("Hi")
}
`, "Game.tgmx", "Kai.tspx")
}

func TestSpxRun(t *testing.T) {
	gopSpxTestEx(t, `
var (
	Kai Kai
	t   Sound
)

run "hzip://open.qiniu.us/weather/res.zip"
`, `
println "Hi"
`, `package main

import (
	fmt "fmt"
	spx "github.com/goplus/gop/cl/internal/spx"
)

type index struct {
	*spx.MyGame
	Kai Kai
	t   spx.Sound
}
type Kai struct {
	spx.Sprite
	*index
}

func (this *index) MainEntry() {
	spx.Gopt_MyGame_Run(this, "hzip://open.qiniu.us/weather/res.zip")
}
func main() {
	spx.Gopt_MyGame_Main(new(index))
}
func (this *Kai) Main() {
	fmt.Println("Hi")
}
`, "index.tgmx", "Kai.tspx")
}

func TestSpx2(t *testing.T) {
	gopSpxTestEx(t, `
println("Hi")
`, `
func onMsg(msg string) {
}
`, `package main

import (
	fmt "fmt"
	spx2 "github.com/goplus/gop/cl/internal/spx2"
)

type Game struct {
	spx2.Game
}

func (this *Game) MainEntry() {
	fmt.Println("Hi")
}
func main() {
	new(Game).Main()
}

type Kai struct {
	spx2.Sprite
	*Game
}

func (this *Kai) onMsg(msg string) {
}
`, "Game.t2gmx", "Kai.t2spx")
}
<|endoftext|>


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

/*
	Package anlz provides the necessary API for making source code analyzis.
*/
package anlzr

import (
	"encoding/xml"

	"github.com/DevMine/srcanlzr/src"
)

type Analyzer interface {
	Analyze(p *src.Project, r *Result) error
}

type Result struct {
	XMLName           xml.Name       `json:"-" xml:"result"`
	ProgLangs         []src.Language `json:"programming_languages" xml:"programming-languages"`
	AverageComplexity int            `json:"average_complexity" xml:"averageComplexity"`
	AverageFuncLen    float32        `json:"average_function_length" xml:"average-function-length"`
	MaxFuncLen        int64          `json:"max_function_length" xml:"max-function-length"`
	MinFuncLen        int64          `json:"min_function_length" xml:"min-function-length"`
	MedianFuncLen     int64          `json:"median_function_length" xml:"median-function-length"`
	TotalLoC          int64          `json:"total_loc" xml:"total-loc"`
}

func RunAnalyzers(p *src.Project, a ...Analyzer) (*Result, error) {
	r := &Result{
		ProgLangs:         p.ProgLangs,
		AverageComplexity: -1,
		AverageFuncLen:    -1.0,
		MaxFuncLen:        -1,
		MinFuncLen:        -1,
		MedianFuncLen:     -1,
		TotalLoC:          -1,
	}

	for _, anlzr := range a {
		err := anlzr.Analyze(p, r)
		if err != nil {
			return nil, err
		}
	}

	return r, nil
}
<commit_msg>anlzr: replace AverageComplexity field by a ComplexityMetric structure<commit_after>// Copyright 2014 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*
	Package anlz provides the necessary API for making source code analyzis.
*/
package anlzr

import (
	"encoding/xml"

	"github.com/DevMine/srcanlzr/src"
)

type Analyzer interface {
	Analyze(p *src.Project, r *Result) error
}

type Result struct {
	XMLName        xml.Name          `json:"-" xml:"result"`
	ProgLangs      []src.Language    `json:"programming_languages" xml:"programming-languages"`
	AverageFuncLen float32           `json:"average_function_length" xml:"average-function-length"`
	MaxFuncLen     int64             `json:"max_function_length" xml:"max-function-length"`
	MinFuncLen     int64             `json:"min_function_length" xml:"min-function-length"`
	MedianFuncLen  int64             `json:"median_function_length" xml:"median-function-length"`
	TotalLoC       int64             `json:"total_loc" xml:"total-loc"`
	Complexity     ComplexityMetrics `json:"complexity"`
}

// Cyclomatic complexity metrics, also known as McCabe metric.
type ComplexityMetrics struct {
	AveragePerFunc float32 `json:"average_per_func"` // Average complexity per function.
	AveragePerFile float32 `json:"average_per_file"` // Average complexity per file.
}

func RunAnalyzers(p *src.Project, a ...Analyzer) (*Result, error) {
	r := &Result{
		ProgLangs:      p.ProgLangs,
		AverageFuncLen: -1.0,
		MaxFuncLen:     -1,
		MinFuncLen:     -1,
		MedianFuncLen:  -1,
		TotalLoC:       -1,
		Complexity:     ComplexityMetrics{AveragePerFunc: -1, AveragePerFile: -1},
	}

	for _, anlzr := range a {
		err := anlzr.Analyze(p, r)
		if err != nil {
			return nil, err
		}
	}

	return r, nil
}
<|endoftext|>


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

package global

import (
	"strconv"
	"strings"

	"v.io/v23/discovery"
	"v.io/v23/naming"
	"v.io/v23/vom"
)

// encodeAdToSuffix encodes the ad.Id and the ad.Attributes into the suffix at
// which we mount the advertisement.
// The format of the generated suffix is id/interfaceName/timestamp/attributes.
//
// TODO(suharshs): Currently only the id and the attributes are encoded; we may
// want to encode the rest of the advertisement someday?
func encodeAdToSuffix(ad *discovery.Advertisement, timestampNs int64) (string, error) {
	b, err := vom.Encode(ad.Attributes)
	if err != nil {
		return "", err
	}
	// Escape suffixDelim to use it as our delimeter between the id and the attrs.
	id := ad.Id.String()
	// InterfaceName can never be empty as per validate.go.
	interfaceName := naming.EncodeAsNameElement(ad.InterfaceName)
	timestamp := strconv.FormatInt(timestampNs, 10)
	attr := naming.EncodeAsNameElement(string(b))
	return naming.Join(id, interfaceName, timestamp, attr), nil
}

// decodeAdFromSuffix decodes in into an advertisement.
// The format of the input suffix is id/interfaceName/timestamp/attributes.
func decodeAdFromSuffix(in string) (*discovery.Advertisement, int64, error) {
	parts := strings.SplitN(in, "/", 4)
	if len(parts) != 4 {
		return nil, 0, NewErrAdInvalidEncoding(nil, in)
	}
	var err error
	var ok bool
	ad := &discovery.Advertisement{}
	if ad.Id, err = discovery.ParseAdId(parts[0]); err != nil {
		return nil, 0, err
	}
	if ad.InterfaceName, ok = naming.DecodeFromNameElement(parts[1]); !ok {
		return nil, 0, NewErrAdInvalidEncoding(nil, in)
	}
	timestampNs, err := strconv.ParseInt(parts[2], 10, 64)
	if err != nil {
		return nil, 0, err
	}
	attrs, ok := naming.DecodeFromNameElement(parts[3])
	if !ok {
		return nil, 0, NewErrAdInvalidEncoding(nil, in)
	}
	if err = vom.Decode([]byte(attrs), &ad.Attributes); err != nil {
		return nil, 0, err
	}
	return ad, timestampNs, nil
}
<commit_msg>TBR: Fix my todo.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package global

import (
	"strconv"
	"strings"

	"v.io/v23/discovery"
	"v.io/v23/naming"
	"v.io/v23/vom"
)

// encodeAdToSuffix encodes the ad.Id and the ad.Attributes into the suffix at
// which we mount the advertisement.
// The format of the generated suffix is id/interfaceName/timestamp/attributes.
//
// TODO(suharshs): Currently Attachments are not encoded in global discovery.
func encodeAdToSuffix(ad *discovery.Advertisement, timestampNs int64) (string, error) {
	b, err := vom.Encode(ad.Attributes)
	if err != nil {
		return "", err
	}
	// Escape suffixDelim to use it as our delimeter between the id and the attrs.
	id := ad.Id.String()
	// InterfaceName can never be empty as per validate.go.
	interfaceName := naming.EncodeAsNameElement(ad.InterfaceName)
	timestamp := strconv.FormatInt(timestampNs, 10)
	attr := naming.EncodeAsNameElement(string(b))
	return naming.Join(id, interfaceName, timestamp, attr), nil
}

// decodeAdFromSuffix decodes in into an advertisement.
// The format of the input suffix is id/interfaceName/timestamp/attributes.
func decodeAdFromSuffix(in string) (*discovery.Advertisement, int64, error) {
	parts := strings.SplitN(in, "/", 4)
	if len(parts) != 4 {
		return nil, 0, NewErrAdInvalidEncoding(nil, in)
	}
	var err error
	var ok bool
	ad := &discovery.Advertisement{}
	if ad.Id, err = discovery.ParseAdId(parts[0]); err != nil {
		return nil, 0, err
	}
	if ad.InterfaceName, ok = naming.DecodeFromNameElement(parts[1]); !ok {
		return nil, 0, NewErrAdInvalidEncoding(nil, in)
	}
	timestampNs, err := strconv.ParseInt(parts[2], 10, 64)
	if err != nil {
		return nil, 0, err
	}
	attrs, ok := naming.DecodeFromNameElement(parts[3])
	if !ok {
		return nil, 0, NewErrAdInvalidEncoding(nil, in)
	}
	if err = vom.Decode([]byte(attrs), &ad.Attributes); err != nil {
		return nil, 0, err
	}
	return ad, timestampNs, nil
}
<|endoftext|>