<commit_before>package main

import (
	"bufio"
	"flag"
	"log"
	"os"
	"sort"
	"time"

	"github.com/hatstand/shinywaffle/metar"
	"github.com/hatstand/shinywaffle/wirelesstag"
	"github.com/wcharczuk/go-chart"
)

var clientSecret = flag.String("secret", "", "OAuth2 client secret for WirelessTag")
var clientId = flag.String("client", "", "OAuth2 client id for WirelessTag")

type point struct {
	x time.Time
	y float64
}

func createSeries(data map[string]map[string][]float64, room string) *chart.TimeSeries {
	var series []point
	for date := range data {
		logs := data[date]
		d, _ := time.Parse("1/2/2006", date)
		for i, l := range logs[room] {
			if l == 0.0 {
				continue
			}
			t := time.Date(d.Year(), d.Month(), d.Day(), i, 0, 0, 0, time.UTC)
			series = append(series, point{
				x: t,
				y: l,
			})
		}
	}
	sort.Slice(series, func(i, j int) bool {
		return series[i].x.Before(series[j].x)
	})
	var tempX []time.Time
	var tempY []float64
	for _, t := range series {
		tempX = append(tempX, t.x)
		tempY = append(tempY, t.y)
	}
	return &chart.TimeSeries{
		XValues: tempX,
		YValues: tempY,
	}
}

func main() {
	flag.Parse()

	data, err := wirelesstag.GetLogs(*clientId, *clientSecret)
	var dates []time.Time
	for k := range data {
		t, _ := time.Parse("1/2/2006", k)
		dates = append(dates, t)
	}
	sort.Slice(dates, func(i, j int) bool {
		return dates[i].Before(dates[j])
	})

	start := dates[0]
	finish := dates[len(dates)-1]

	log.Printf("Requesting METARs from %v to %v", start, finish)

	METARs, err := metar.FetchMETARs(start, finish)
	if err != nil {
		log.Fatalf("Failed to fetch METARs: %v", err)
	}

	var metarX []time.Time
	var metarY []float64
	for _, m := range METARs {
		metarX = append(metarX, m.DateTime)
		metarY = append(metarY, float64(m.Temperature))
	}

	graph := chart.Chart{
		XAxis: chart.XAxis{
			Style: chart.Style{
				Show: true,
			},
		},
		Series: []chart.Series{
			chart.TimeSeries{
				XValues: metarX,
				YValues: metarY,
			},
			createSeries(data, "Hall"),
			createSeries(data, "Bedroom"),
			createSeries(data, "Living Room"),
			createSeries(data, "Study"),
		},
	}

	w := bufio.NewWriter(os.Stdout)
	defer w.Flush()
	graph.Render(chart.PNG, w)
}
<commit_msg>Sort METAR data too<commit_after>package main

import (
	"bufio"
	"flag"
	"log"
	"os"
	"sort"
	"time"

	"github.com/hatstand/shinywaffle/metar"
	"github.com/hatstand/shinywaffle/wirelesstag"
	"github.com/wcharczuk/go-chart"
)

var clientSecret = flag.String("secret", "", "OAuth2 client secret for WirelessTag")
var clientId = flag.String("client", "", "OAuth2 client id for WirelessTag")

type point struct {
	x time.Time
	y float64
}

func createSeries(data map[string]map[string][]float64, room string) *chart.TimeSeries {
	var series []point
	for date := range data {
		logs := data[date]
		d, _ := time.Parse("1/2/2006", date)
		for i, l := range logs[room] {
			if l == 0.0 {
				continue
			}
			t := time.Date(d.Year(), d.Month(), d.Day(), i, 0, 0, 0, time.UTC)
			series = append(series, point{
				x: t,
				y: l,
			})
		}
	}
	sort.Slice(series, func(i, j int) bool {
		return series[i].x.Before(series[j].x)
	})
	var tempX []time.Time
	var tempY []float64
	for _, t := range series {
		tempX = append(tempX, t.x)
		tempY = append(tempY, t.y)
	}
	return &chart.TimeSeries{
		XValues: tempX,
		YValues: tempY,
	}
}

func createSeriesFromMETAR(metar []*metar.METAR) *chart.TimeSeries {
	sort.Slice(metar, func(i, j int) bool {
		return metar[i].DateTime.Before(metar[j].DateTime)
	})
	var x []time.Time
	var y []float64
	for _, m := range metar {
		x = append(x, m.DateTime)
		y = append(y, float64(m.Temperature))
	}
	return &chart.TimeSeries{
		XValues: x,
		YValues: y,
	}
}

func main() {
	flag.Parse()

	data, err := wirelesstag.GetLogs(*clientId, *clientSecret)
	var dates []time.Time
	for k := range data {
		t, _ := time.Parse("1/2/2006", k)
		dates = append(dates, t)
	}
	sort.Slice(dates, func(i, j int) bool {
		return dates[i].Before(dates[j])
	})

	start := dates[0]
	finish := dates[len(dates)-1]

	log.Printf("Requesting METARs from %v to %v", start, finish)

	METARs, err := metar.FetchMETARs(start, finish)
	if err != nil {
		log.Fatalf("Failed to fetch METARs: %v", err)
	}

	graph := chart.Chart{
		XAxis: chart.XAxis{
			Style: chart.Style{
				Show: true,
			},
		},
		Series: []chart.Series{
			createSeriesFromMETAR(METARs),
			createSeries(data, "Hall"),
			createSeries(data, "Bedroom"),
			createSeries(data, "Living Room"),
			createSeries(data, "Study"),
		},
	}

	w := bufio.NewWriter(os.Stdout)
	defer w.Flush()
	graph.Render(chart.PNG, w)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"
)

func main()  {
	fmt.Println("Problem 4");
	t0 := time.Now()
	var number int = 0;
	var found bool = true;
	for i:=2520; ; i += 20 {
		found = true;
		for j:=2; j<=20; j++ {
			if i % j !=0 {
				found = false;
			}
		}
		if found == true {
			number = i;
			break;
		}

	}
	fmt.Println("The number is:",number);
	t1 := time.Now()
	fmt.Printf("The call took %v to run.\n", t1.Sub(t0))
}
<commit_msg>Update main.go<commit_after>package main

import (
	"fmt"
	"time"
)

func main()  {
	fmt.Println("Problem 5");
	t0 := time.Now()
	var number int = 0;
	var found bool = true;
	for i:=2520; ; i += 20 {
		found = true;
		for j:=2; j<=20; j++ {
			if i % j !=0 {
				found = false;
			}
		}
		if found == true {
			number = i;
			break;
		}

	}
	fmt.Println("The number is:",number);
	t1 := time.Now()
	fmt.Printf("The call took %v to run.\n", t1.Sub(t0))
}
<|endoftext|>


<commit_before>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// 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 fileserver

import (
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/caddyserver/caddy/v2"
	"github.com/dustin/go-humanize"
)

func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, root, urlPath string, repl *caddy.Replacer) browseTemplateContext {
	filesToHide := fsrv.transformHidePaths(repl)

	var dirCount, fileCount int
	fileInfos := []fileInfo{}

	for _, f := range files {
		name := f.Name()

		if fileHidden(name, filesToHide) {
			continue
		}

		isDir := f.IsDir() || isSymlinkTargetDir(f, root, urlPath)

		if isDir {
			name += "/"
			dirCount++
		} else {
			fileCount++
		}

		u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name

		fileInfos = append(fileInfos, fileInfo{
			IsDir:     isDir,
			IsSymlink: isSymlink(f),
			Name:      f.Name(),
			Size:      f.Size(),
			URL:       u.String(),
			ModTime:   f.ModTime().UTC(),
			Mode:      f.Mode(),
		})
	}

	return browseTemplateContext{
		Name:     path.Base(urlPath),
		Path:     urlPath,
		CanGoUp:  canGoUp,
		Items:    fileInfos,
		NumDirs:  dirCount,
		NumFiles: fileCount,
	}
}

// browseTemplateContext provides the template context for directory listings.
type browseTemplateContext struct {
	// The name of the directory (the last element of the path).
	Name string `json:"name"`

	// The full path of the request.
	Path string `json:"path"`

	// Whether the parent directory is browseable.
	CanGoUp bool `json:"can_go_up"`

	// The items (files and folders) in the path.
	Items []fileInfo `json:"items,omitempty"`

	// If ≠0 then Items starting from that many elements.
	Offset int `json:"offset,omitempty"`

	// If ≠0 then Items have been limited to that many elements.
	Limit int `json:"limit,omitempty"`

	// The number of directories in the listing.
	NumDirs int `json:"num_dirs"`

	// The number of files (items that aren't directories) in the listing.
	NumFiles int `json:"num_files"`

	// Sort column used
	Sort string `json:"sort,omitempty"`

	// Sorting order
	Order string `json:"order,omitempty"`
}

// Breadcrumbs returns l.Path where every element maps
// the link to the text to display.
func (l browseTemplateContext) Breadcrumbs() []crumb {
	if len(l.Path) == 0 {
		return []crumb{}
	}

	// skip trailing slash
	lpath := l.Path
	if lpath[len(lpath)-1] == '/' {
		lpath = lpath[:len(lpath)-1]
	}

	parts := strings.Split(lpath, "/")
	result := make([]crumb, len(parts))
	for i, p := range parts {
		if i == 0 && p == "" {
			p = "/"
		}
		lnk := strings.Repeat("../", len(parts)-i-1)
		result[i] = crumb{Link: lnk, Text: p}
	}

	return result
}

func (l *browseTemplateContext) applySortAndLimit(sortParam, orderParam, limitParam string, offsetParam string) {
	l.Sort = sortParam
	l.Order = orderParam

	if l.Order == "desc" {
		switch l.Sort {
		case sortByName:
			sort.Sort(sort.Reverse(byName(*l)))
		case sortByNameDirFirst:
			sort.Sort(sort.Reverse(byNameDirFirst(*l)))
		case sortBySize:
			sort.Sort(sort.Reverse(bySize(*l)))
		case sortByTime:
			sort.Sort(sort.Reverse(byTime(*l)))
		}
	} else {
		switch l.Sort {
		case sortByName:
			sort.Sort(byName(*l))
		case sortByNameDirFirst:
			sort.Sort(byNameDirFirst(*l))
		case sortBySize:
			sort.Sort(bySize(*l))
		case sortByTime:
			sort.Sort(byTime(*l))
		}
	}

	if offsetParam != "" {
		offset, _ := strconv.Atoi(offsetParam)
		if offset > 0 && offset <= len(l.Items) {
			l.Items = l.Items[offset:]
			l.Offset = offset
		}
	}

	if limitParam != "" {
		limit, _ := strconv.Atoi(limitParam)

		if limit > 0 && limit <= len(l.Items) {
			l.Items = l.Items[:limit]
			l.Limit = limit
		}
	}
}

// crumb represents part of a breadcrumb menu,
// pairing a link with the text to display.
type crumb struct {
	Link, Text string
}

// fileInfo contains serializable information
// about a file or directory.
type fileInfo struct {
	Name      string      `json:"name"`
	Size      int64       `json:"size"`
	URL       string      `json:"url"`
	ModTime   time.Time   `json:"mod_time"`
	Mode      os.FileMode `json:"mode"`
	IsDir     bool        `json:"is_dir"`
	IsSymlink bool        `json:"is_symlink"`
}

// HumanSize returns the size of the file as a
// human-readable string in IEC format (i.e.
// power of 2 or base 1024).
func (fi fileInfo) HumanSize() string {
	return humanize.IBytes(uint64(fi.Size))
}

// HumanModTime returns the modified time of the file
// as a human-readable string given by format.
func (fi fileInfo) HumanModTime(format string) string {
	return fi.ModTime.Format(format)
}

type (
	byName         browseTemplateContext
	byNameDirFirst browseTemplateContext
	bySize         browseTemplateContext
	byTime         browseTemplateContext
)

func (l byName) Len() int      { return len(l.Items) }
func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

func (l byName) Less(i, j int) bool {
	return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
}

func (l byNameDirFirst) Len() int      { return len(l.Items) }
func (l byNameDirFirst) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

func (l byNameDirFirst) Less(i, j int) bool {
	// sort by name if both are dir or file
	if l.Items[i].IsDir == l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	}
	// sort dir ahead of file
	return l.Items[i].IsDir
}

func (l bySize) Len() int      { return len(l.Items) }
func (l bySize) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

func (l bySize) Less(i, j int) bool {
	const directoryOffset = -1 << 31 // = -math.MinInt32

	iSize, jSize := l.Items[i].Size, l.Items[j].Size

	// directory sizes depend on the file system; to
	// provide a consistent experience, put them up front
	// and sort them by name
	if l.Items[i].IsDir {
		iSize = directoryOffset
	}
	if l.Items[j].IsDir {
		jSize = directoryOffset
	}
	if l.Items[i].IsDir && l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	}

	return iSize < jSize
}

func (l byTime) Len() int           { return len(l.Items) }
func (l byTime) Swap(i, j int)      { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }
func (l byTime) Less(i, j int) bool { return l.Items[i].ModTime.Before(l.Items[j].ModTime) }

const (
	sortByName         = "name"
	sortByNameDirFirst = "name_dir_first"
	sortBySize         = "size"
	sortByTime         = "time"
)
<commit_msg>fileserver: Fix browse name_dir_first sorting (#4218)<commit_after>// Copyright 2015 Matthew Holt and The Caddy Authors
//
// 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 fileserver

import (
	"net/url"
	"os"
	"path"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/caddyserver/caddy/v2"
	"github.com/dustin/go-humanize"
)

func (fsrv *FileServer) directoryListing(files []os.FileInfo, canGoUp bool, root, urlPath string, repl *caddy.Replacer) browseTemplateContext {
	filesToHide := fsrv.transformHidePaths(repl)

	var dirCount, fileCount int
	fileInfos := []fileInfo{}

	for _, f := range files {
		name := f.Name()

		if fileHidden(name, filesToHide) {
			continue
		}

		isDir := f.IsDir() || isSymlinkTargetDir(f, root, urlPath)

		if isDir {
			name += "/"
			dirCount++
		} else {
			fileCount++
		}

		u := url.URL{Path: "./" + name} // prepend with "./" to fix paths with ':' in the name

		fileInfos = append(fileInfos, fileInfo{
			IsDir:     isDir,
			IsSymlink: isSymlink(f),
			Name:      f.Name(),
			Size:      f.Size(),
			URL:       u.String(),
			ModTime:   f.ModTime().UTC(),
			Mode:      f.Mode(),
		})
	}

	return browseTemplateContext{
		Name:     path.Base(urlPath),
		Path:     urlPath,
		CanGoUp:  canGoUp,
		Items:    fileInfos,
		NumDirs:  dirCount,
		NumFiles: fileCount,
	}
}

// browseTemplateContext provides the template context for directory listings.
type browseTemplateContext struct {
	// The name of the directory (the last element of the path).
	Name string `json:"name"`

	// The full path of the request.
	Path string `json:"path"`

	// Whether the parent directory is browseable.
	CanGoUp bool `json:"can_go_up"`

	// The items (files and folders) in the path.
	Items []fileInfo `json:"items,omitempty"`

	// If ≠0 then Items starting from that many elements.
	Offset int `json:"offset,omitempty"`

	// If ≠0 then Items have been limited to that many elements.
	Limit int `json:"limit,omitempty"`

	// The number of directories in the listing.
	NumDirs int `json:"num_dirs"`

	// The number of files (items that aren't directories) in the listing.
	NumFiles int `json:"num_files"`

	// Sort column used
	Sort string `json:"sort,omitempty"`

	// Sorting order
	Order string `json:"order,omitempty"`
}

// Breadcrumbs returns l.Path where every element maps
// the link to the text to display.
func (l browseTemplateContext) Breadcrumbs() []crumb {
	if len(l.Path) == 0 {
		return []crumb{}
	}

	// skip trailing slash
	lpath := l.Path
	if lpath[len(lpath)-1] == '/' {
		lpath = lpath[:len(lpath)-1]
	}

	parts := strings.Split(lpath, "/")
	result := make([]crumb, len(parts))
	for i, p := range parts {
		if i == 0 && p == "" {
			p = "/"
		}
		lnk := strings.Repeat("../", len(parts)-i-1)
		result[i] = crumb{Link: lnk, Text: p}
	}

	return result
}

func (l *browseTemplateContext) applySortAndLimit(sortParam, orderParam, limitParam string, offsetParam string) {
	l.Sort = sortParam
	l.Order = orderParam

	if l.Order == "desc" {
		switch l.Sort {
		case sortByName:
			sort.Sort(sort.Reverse(byName(*l)))
		case sortByNameDirFirst:
			sort.Sort(sort.Reverse(byNameDirFirst(*l)))
		case sortBySize:
			sort.Sort(sort.Reverse(bySize(*l)))
		case sortByTime:
			sort.Sort(sort.Reverse(byTime(*l)))
		}
	} else {
		switch l.Sort {
		case sortByName:
			sort.Sort(byName(*l))
		case sortByNameDirFirst:
			sort.Sort(byNameDirFirst(*l))
		case sortBySize:
			sort.Sort(bySize(*l))
		case sortByTime:
			sort.Sort(byTime(*l))
		}
	}

	if offsetParam != "" {
		offset, _ := strconv.Atoi(offsetParam)
		if offset > 0 && offset <= len(l.Items) {
			l.Items = l.Items[offset:]
			l.Offset = offset
		}
	}

	if limitParam != "" {
		limit, _ := strconv.Atoi(limitParam)

		if limit > 0 && limit <= len(l.Items) {
			l.Items = l.Items[:limit]
			l.Limit = limit
		}
	}
}

// crumb represents part of a breadcrumb menu,
// pairing a link with the text to display.
type crumb struct {
	Link, Text string
}

// fileInfo contains serializable information
// about a file or directory.
type fileInfo struct {
	Name      string      `json:"name"`
	Size      int64       `json:"size"`
	URL       string      `json:"url"`
	ModTime   time.Time   `json:"mod_time"`
	Mode      os.FileMode `json:"mode"`
	IsDir     bool        `json:"is_dir"`
	IsSymlink bool        `json:"is_symlink"`
}

// HumanSize returns the size of the file as a
// human-readable string in IEC format (i.e.
// power of 2 or base 1024).
func (fi fileInfo) HumanSize() string {
	return humanize.IBytes(uint64(fi.Size))
}

// HumanModTime returns the modified time of the file
// as a human-readable string given by format.
func (fi fileInfo) HumanModTime(format string) string {
	return fi.ModTime.Format(format)
}

type (
	byName         browseTemplateContext
	byNameDirFirst browseTemplateContext
	bySize         browseTemplateContext
	byTime         browseTemplateContext
)

func (l byName) Len() int      { return len(l.Items) }
func (l byName) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

func (l byName) Less(i, j int) bool {
	return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
}

func (l byNameDirFirst) Len() int      { return len(l.Items) }
func (l byNameDirFirst) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

func (l byNameDirFirst) Less(i, j int) bool {
	// sort by name if both are dir or file
	if l.Items[i].IsDir == l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	}
	// sort dir ahead of file
	return l.Items[i].IsDir
}

func (l bySize) Len() int      { return len(l.Items) }
func (l bySize) Swap(i, j int) { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }

func (l bySize) Less(i, j int) bool {
	const directoryOffset = -1 << 31 // = -math.MinInt32

	iSize, jSize := l.Items[i].Size, l.Items[j].Size

	// directory sizes depend on the file system; to
	// provide a consistent experience, put them up front
	// and sort them by name
	if l.Items[i].IsDir {
		iSize = directoryOffset
	}
	if l.Items[j].IsDir {
		jSize = directoryOffset
	}
	if l.Items[i].IsDir && l.Items[j].IsDir {
		return strings.ToLower(l.Items[i].Name) < strings.ToLower(l.Items[j].Name)
	}

	return iSize < jSize
}

func (l byTime) Len() int           { return len(l.Items) }
func (l byTime) Swap(i, j int)      { l.Items[i], l.Items[j] = l.Items[j], l.Items[i] }
func (l byTime) Less(i, j int) bool { return l.Items[i].ModTime.Before(l.Items[j].ModTime) }

const (
	sortByName         = "name"
	sortByNameDirFirst = "namedirfirst"
	sortBySize         = "size"
	sortByTime         = "time"
)
<|endoftext|>


<commit_before>package git_test // to avoid import cycles

import (
	"testing"
	"time"

	. "github.com/github/git-lfs/git"
	"github.com/github/git-lfs/test"
	"github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/assert"
)

func TestRecentBranches(t *testing.T) {
	repo := test.NewRepo(t)
	repo.Pushd()
	defer func() {
		repo.Popd()
		repo.Cleanup()
	}()

	now := time.Now()
	// test commits; we'll just modify the same file each time since we're
	// only interested in branches & dates
	inputs := []*test.CommitInput{
		&test.CommitInput{ // 0
			CommitDate: now.AddDate(0, 0, -20),
			Files: []*test.FileInput{
				&test.FileInput{"file1.txt", 20, nil},
			},
		},
		&test.CommitInput{ // 1
			CommitDate: now.AddDate(0, 0, -15),
			NewBranch:  "excluded_branch", // new branch & tag but too old
			Tags:       []string{"excluded_tag"},
			Files: []*test.FileInput{
				&test.FileInput{"file1.txt", 25, nil},
			},
		},
		&test.CommitInput{ // 2
			CommitDate:     now.AddDate(0, 0, -12),
			ParentBranches: []string{"master"}, // back on master
			Files: []*test.FileInput{
				&test.FileInput{"file1.txt", 30, nil},
			},
		},
		&test.CommitInput{ // 3
			CommitDate: now.AddDate(0, 0, -6),
			NewBranch:  "included_branch", // new branch within 7 day limit
			Files: []*test.FileInput{
				&test.FileInput{"file1.txt", 32, nil},
			},
		},
		&test.CommitInput{ // 4
			CommitDate: now.AddDate(0, 0, -3),
			NewBranch:  "included_branch_2", // new branch within 7 day limit
			Files: []*test.FileInput{
				&test.FileInput{"file1.txt", 36, nil},
			},
		},
		&test.CommitInput{ // 5
			// Final commit, current date/time
			ParentBranches: []string{"master"}, // back on master
			Files: []*test.FileInput{
				&test.FileInput{"file1.txt", 21, nil},
			},
		},
	}
	outputs := repo.AddCommits(inputs)

	// Add a couple of remotes and push some branches
	repo.AddRemote("origin")
	repo.AddRemote("upstream")

	test.RunGitCommand(t, true, "push", "origin", "master")
	test.RunGitCommand(t, true, "push", "origin", "excluded_branch")
	test.RunGitCommand(t, true, "push", "origin", "included_branch")
	test.RunGitCommand(t, true, "push", "upstream", "master")
	test.RunGitCommand(t, true, "push", "upstream", "included_branch_2")

	// Recent, local only
	refs, err := RecentBranches(now.AddDate(0, 0, -7), false, "")
	assert.Equal(t, nil, err)
	expectedRefs := []*Ref{
		&Ref{"master", RefTypeLocalBranch, outputs[5].Sha},
		&Ref{"included_branch_2", RefTypeLocalBranch, outputs[4].Sha},
		&Ref{"included_branch", RefTypeLocalBranch, outputs[3].Sha},
	}
	assert.Equal(t, expectedRefs, refs, "Refs should be correct")

	// Recent, remotes too (all of them)
	refs, err = RecentBranches(now.AddDate(0, 0, -7), true, "")
	assert.Equal(t, nil, err)
	expectedRefs = []*Ref{
		&Ref{"master", RefTypeLocalBranch, outputs[5].Sha},
		&Ref{"upstream/master", RefTypeRemoteBranch, outputs[5].Sha},
		&Ref{"origin/master", RefTypeRemoteBranch, outputs[5].Sha},
		&Ref{"upstream/included_branch_2", RefTypeRemoteBranch, outputs[4].Sha},
		&Ref{"included_branch_2", RefTypeLocalBranch, outputs[4].Sha},
		&Ref{"included_branch", RefTypeLocalBranch, outputs[3].Sha},
		&Ref{"origin/included_branch", RefTypeRemoteBranch, outputs[3].Sha},
	}
	assert.Equal(t, expectedRefs, refs, "Refs should be correct")

	// Recent, only single remote
	refs, err = RecentBranches(now.AddDate(0, 0, -7), true, "origin")
	assert.Equal(t, nil, err)
	expectedRefs = []*Ref{
		&Ref{"master", RefTypeLocalBranch, outputs[5].Sha},
		&Ref{"origin/master", RefTypeRemoteBranch, outputs[5].Sha},
		&Ref{"included_branch_2", RefTypeLocalBranch, outputs[4].Sha},
		&Ref{"included_branch", RefTypeLocalBranch, outputs[3].Sha},
		&Ref{"origin/included_branch", RefTypeRemoteBranch, outputs[3].Sha},
	}
	assert.Equal(t, expectedRefs, refs, "Refs should be correct")
}
<commit_msg>ラララララ ラー ウウウ フフフ<commit_after>package git_test // to avoid import cycles

import (
	"testing"
	"time"

	. "github.com/github/git-lfs/git"
	"github.com/github/git-lfs/test"
	"github.com/github/git-lfs/vendor/_nuts/github.com/technoweenie/assert"
)

func TestRecentBranches(t *testing.T) {
	repo := test.NewRepo(t)
	repo.Pushd()
	defer func() {
		repo.Popd()
		repo.Cleanup()
	}()

	now := time.Now()
	// test commits; we'll just modify the same file each time since we're
	// only interested in branches & dates
	inputs := []*test.CommitInput{
		{ // 0
			CommitDate: now.AddDate(0, 0, -20),
			Files: []*test.FileInput{
				{"file1.txt", 20, nil},
			},
		},
		{ // 1
			CommitDate: now.AddDate(0, 0, -15),
			NewBranch:  "excluded_branch", // new branch & tag but too old
			Tags:       []string{"excluded_tag"},
			Files: []*test.FileInput{
				{"file1.txt", 25, nil},
			},
		},
		{ // 2
			CommitDate:     now.AddDate(0, 0, -12),
			ParentBranches: []string{"master"}, // back on master
			Files: []*test.FileInput{
				{"file1.txt", 30, nil},
			},
		},
		{ // 3
			CommitDate: now.AddDate(0, 0, -6),
			NewBranch:  "included_branch", // new branch within 7 day limit
			Files: []*test.FileInput{
				{"file1.txt", 32, nil},
			},
		},
		{ // 4
			CommitDate: now.AddDate(0, 0, -3),
			NewBranch:  "included_branch_2", // new branch within 7 day limit
			Files: []*test.FileInput{
				{"file1.txt", 36, nil},
			},
		},
		{ // 5
			// Final commit, current date/time
			ParentBranches: []string{"master"}, // back on master
			Files: []*test.FileInput{
				{"file1.txt", 21, nil},
			},
		},
	}
	outputs := repo.AddCommits(inputs)

	// Add a couple of remotes and push some branches
	repo.AddRemote("origin")
	repo.AddRemote("upstream")

	test.RunGitCommand(t, true, "push", "origin", "master")
	test.RunGitCommand(t, true, "push", "origin", "excluded_branch")
	test.RunGitCommand(t, true, "push", "origin", "included_branch")
	test.RunGitCommand(t, true, "push", "upstream", "master")
	test.RunGitCommand(t, true, "push", "upstream", "included_branch_2")

	// Recent, local only
	refs, err := RecentBranches(now.AddDate(0, 0, -7), false, "")
	assert.Equal(t, nil, err)
	expectedRefs := []*Ref{
		&Ref{"master", RefTypeLocalBranch, outputs[5].Sha},
		&Ref{"included_branch_2", RefTypeLocalBranch, outputs[4].Sha},
		&Ref{"included_branch", RefTypeLocalBranch, outputs[3].Sha},
	}
	assert.Equal(t, expectedRefs, refs, "Refs should be correct")

	// Recent, remotes too (all of them)
	refs, err = RecentBranches(now.AddDate(0, 0, -7), true, "")
	assert.Equal(t, nil, err)
	expectedRefs = []*Ref{
		&Ref{"master", RefTypeLocalBranch, outputs[5].Sha},
		&Ref{"upstream/master", RefTypeRemoteBranch, outputs[5].Sha},
		&Ref{"origin/master", RefTypeRemoteBranch, outputs[5].Sha},
		&Ref{"upstream/included_branch_2", RefTypeRemoteBranch, outputs[4].Sha},
		&Ref{"included_branch_2", RefTypeLocalBranch, outputs[4].Sha},
		&Ref{"included_branch", RefTypeLocalBranch, outputs[3].Sha},
		&Ref{"origin/included_branch", RefTypeRemoteBranch, outputs[3].Sha},
	}
	assert.Equal(t, expectedRefs, refs, "Refs should be correct")

	// Recent, only single remote
	refs, err = RecentBranches(now.AddDate(0, 0, -7), true, "origin")
	assert.Equal(t, nil, err)
	expectedRefs = []*Ref{
		&Ref{"master", RefTypeLocalBranch, outputs[5].Sha},
		&Ref{"origin/master", RefTypeRemoteBranch, outputs[5].Sha},
		&Ref{"included_branch_2", RefTypeLocalBranch, outputs[4].Sha},
		&Ref{"included_branch", RefTypeLocalBranch, outputs[3].Sha},
		&Ref{"origin/included_branch", RefTypeRemoteBranch, outputs[3].Sha},
	}
	assert.Equal(t, expectedRefs, refs, "Refs should be correct")
}
<|endoftext|>


<commit_before>// go-ari-proxy is an implementation of the go-ari-library used to
// connect to the Asterisk REST Interface for delivery of Events and
// Commands across a message bus.
package main

import (
	"bytes"
	"code.google.com/p/go.net/websocket"
	"encoding/json"
	"flag"
	"github.com/nvisibleinc/go-ari-library"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// Var config contains a Config struct to hold the proxy configuration file.
var (
	config         Config            // main proxy configuration structure
	client         = &http.Client{}  // connection for Commands to ARI
	proxyInstances *proxyInstanceMap // maps the per-dialog proxy instances
	Debug          *log.Logger
	Info           *log.Logger
	Warning        *log.Logger
	Error          *log.Logger
)

// signalCatcher is a function to allows us to stop the application through an
// operating system signal.
func signalCatcher() {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	sig := <-ch
	log.Printf("Signal received: %v", sig)
	os.Exit(0)
}

// Init parses the configuration file by unmarshaling it into a Config struct.
func init() {
	var err error

	// Setup our logging interfaces
	Debug = ari.InitLogger(os.Stdout, "DEBUG")
	Info = ari.InitLogger(os.Stdout, "INFO")
	Warning = ari.InitLogger(os.Stdout, "WARNING")
	Error = ari.InitLogger(os.Stderr, "ERROR")

	// parse the configuration file and get data from it
	Info.Println("Loading configuration for proxy.")
	configpath := flag.String("config", "./config.json", "Path to config file")

	Debug.Println("Parsing the configuration file.")
	flag.Parse()
	Debug.Println("Reading in the configuration from the file.")
	configfile, err := ioutil.ReadFile(*configpath)
	if err != nil {
		Error.Fatal(err)
	}
	// read in the configuration file and unmarshal the json, storing it in 'config'
	Debug.Println("Unmarshaling proxy configuration.")
	json.Unmarshal(configfile, &config)
	Debug.Println(&config)
	Debug.Println("Initialize the proxy instance map.")
	proxyInstances = NewproxyInstanceMap() // initialize a new proxy instance map
}

func main() {
	// Setup a new Event producer and Command consumer for every application
	// we've configured in the configuration file.
	Info.Println("Initializing the message bus.")
	ari.InitBus(config.MessageBus, config.BusConfig)
	for _, app := range config.Applications {
		/*
			Create a new producer which is responsible for the initial topic on the message bus which is used
			to signal the setup of per-application instances. All applications listen to this topic in order to
			be provided the information to setup the ownership of per dialog application instances.
		*/
		Info.Printf("Initializing signalling bus for application %s", app)
		producer := ari.InitProducer(app) // Initialize a new producer channel using the ari.InitProducer function.
		Info.Printf("Starting event handler for application %s", app)
		go runEventHandler(app, producer) // create new websocket connection for every application and pass the producer channel
	}

	go signalCatcher() // listen for os signal to stop the application
	select {}
}

// runEventHandler sets up a websocket connection to an ARI application.
func runEventHandler(s string, producer chan []byte) {
	// Connect to the websocket backend (ARI)
	var ariMessage string
	url := strings.Join([]string{config.WebsocketURL, "?app=", s, "&api_key=", config.WSUser, ":", config.WSPassword}, "")

	Info.Printf("Attempting to connect to ARI websocket at: %s", url)
	ws, err := websocket.Dial(url, "ari", config.Origin)
	if err != nil {
		log.Fatal(err)
	}

	// Start the producer loop. Every message received from the websocket is
	// passed to the PublishMessage() function.
	Info.Printf("Starting producer loop for application %s", s)
	for {
		err = websocket.Message.Receive(ws, &ariMessage) // accept the message from the websocket
		if err != nil {
			log.Fatal(err)
		}
		go PublishMessage(ariMessage, producer) // publish message to the producer channel
	}
}

// PublishMessage takes an ARI event from the websocket and places it on the
// producer channel.
// Accepts two arguments:
// * a string containing the ARI message
// * a producer channel
func PublishMessage(ariMessage string, producer chan []byte) {
	// unmarshal into an ari.Event so we can append some extra information
	var info eventInfo
	var message ari.Event
	var pi *proxyInstance
	var exists bool = false
	json.Unmarshal([]byte(ariMessage), &message)
	json.Unmarshal([]byte(ariMessage), &info)
	message.ServerID = config.ServerID
	message.Timestamp = time.Now()
	message.ARI_Body = ariMessage

	switch {
	case info.Type == "StasisStart":

		// Check to see if the new channel was already in the map, which means it
		// was created by an originate with ID
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			break
		}
		// since we're starting a new application instance, create the proxy side
		dialogID := ari.UUID()
		Info.Println("New StasisStart found. Created new dialogID of ", dialogID)
		as, err := json.Marshal(ari.AppStart{Application: info.Application, DialogID: dialogID, ServerID: config.ServerID})
		producer <- as

		// TODO: this sleep is required to allow the application time to spin up. In the future we likely want
		// to implement some sort of feedback mechanism in order to remove this sleep timer.
		time.Sleep(50 * time.Millisecond)
		if err != nil {
			return
		}

		Info.Printf("Created new proxy instance mapping for dialog '%s' and channel '%s'", dialogID, info.Channel.ID)
		pi = NewProxyInstance(dialogID)         // create new proxy instance for the dialog
		proxyInstances.Add(info.Channel.ID, pi) // add the dialog to the proxyInstances map to track its life
		exists = true

	case info.Type == "StasisEnd":
		Info.Printf("Ending application instance for channel '%s'", info.Channel.ID)
		// on application end, perform clean up checks
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeAllObjects()
		}

	case info.Type == "BridgeDestroyed":
		pi, exists = proxyInstances.Get(info.Bridge.ID)
		if exists {
			pi.removeObject(info.Bridge.ID)
		}

	case info.Type == "ChannelDestroyed":
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeObject(info.Channel.ID)
		}

	// check if prefix is part of the minChan{} struct
	case strings.HasPrefix(info.Type, "Channel"):
		pi, exists = proxyInstances.Get(info.Channel.ID)

	// check if prefix is part of the minBridge{} struct
	case strings.HasPrefix(message.Type, "Bridge"):
		pi, exists = proxyInstances.Get(info.Bridge.ID)

	// check if prefix is part of the minPlay{} struct
	case strings.HasPrefix(message.Type, "Playback"):
		pi, exists = proxyInstances.Get(info.Playback.ID)

	// check if prefix is part of the minRec{} struct (this one uses a Name instead of ID for some reason)
	case strings.HasPrefix(message.Type, "Recording"):
		pi, exists = proxyInstances.Get(info.Recording.Name)

	default:
		Warning.Println("No handler for event type")
		//pi, exists = proxyInstances[]
		// if not matching, then we need to perform checks against the
		// existing map to determine where to send this ARI message.
	}

	// marshal the message back into a string
	busMessage, err := json.Marshal(message)
	if err != nil {
		Error.Println(err)
		return
	}
	Debug.Printf("Bus Data:\n%s\n", busMessage)

	// push the busMessage onto the producer channel
	if exists {
		pi.Events <- busMessage
	}
}

// Add inserts a proxy instance into the global map of active proxy instances.
func (p *proxyInstanceMap) Add(id string, pi *proxyInstance) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	p.instanceMap[id] = pi
}

// Get returns a proxy instance from the global map of active proxy instances.
// returns nil if not found.
func (p *proxyInstanceMap) Get(id string) (*proxyInstance, bool) {
	p.mapLock.RLock()
	defer p.mapLock.RUnlock()
	pi, ok := p.instanceMap[id]
	if !ok {
		return nil, false
	}
	return pi, true
}

// Remove deletes an entry in the global proxyInstanceMap
func (p *proxyInstanceMap) Remove(id string) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	delete(p.instanceMap, id)
}

// shutDown closes the quit channel to signal all of a ProxyInstance's goroutines
// to return
func (p *proxyInstance) shutDown() {
	select {
	case _, ok := (<-p.quit):
		if !ok {
			return
		}
	default:
		close(p.quit)
	}
}

// addObject adds an object reference to the proxyInstance mapping
func (p *proxyInstance) addObject(id string) {
	for i := range p.ariObjects {
		if p.ariObjects[i] == id {
			//object already is associated with this proxyInstance
			return
		}
	}
	p.ariObjects = append(p.ariObjects, id)
	proxyInstances.Add(id, p)

}

// removeObject removes an object reference from the proxyInstance mapping
func (p *proxyInstance) removeObject(id string) {
	// remove an object from the map.
	for i := range p.ariObjects {
		if p.ariObjects[i] == id {
			// rewrite the p.ariObjects string slice to append all values up to
			// the index value of 'i', and all values of 'i'+1 and later.
			p.ariObjects = append(p.ariObjects[:i], p.ariObjects[i+1:]...)
		}
	}
	// remove the instance from our tracking map
	proxyInstances.Remove(id)

	// if there are no more objects, shut'rdown
	if len(p.ariObjects) == 0 {
		p.shutDown()
	}
}

// removeAllObjects will remove all object references from the proxyInstance mapping
func (p *proxyInstance) removeAllObjects() {
	// remove all objects from the map as our application is shutting down.
	for _, obj := range p.ariObjects {
		proxyInstances.Remove(obj)
	}
	p.shutDown() // destroy the application / proxy instance
}

// runCommandConsumer starts the consumer for accepting Commands from
// applications.
func (p *proxyInstance) runCommandConsumer(dialogID string) {
	commandTopic := strings.Join([]string{"commands", dialogID}, "_")
	responseTopic := strings.Join([]string{"responses", dialogID}, "_")
	Debug.Println("Topics are:", commandTopic, " ", responseTopic)
	p.responseChannel = ari.InitProducer(responseTopic)

	// waits for the TopicExists function to return a channel
	select {
	case <-ari.TopicExists(commandTopic):
		p.commandChannel = ari.InitConsumer(commandTopic)
	case <-time.After(10 * time.Second):
		// if the application instance hasn't come up after a period of time, gracefully end the proxy instance
		p.removeAllObjects()
		return
	}

	for {
		select {
		case jsonCommand := <-p.commandChannel:
			go p.processCommand(jsonCommand, p.responseChannel)
		case <-p.quit:
			return
		}
	}
}

// processCommand processes commands from applications and submits them to the
// REST interface.
func (p *proxyInstance) processCommand(jsonCommand []byte, responseProducer chan []byte) {
	var c ari.Command
	var r ari.CommandResponse
	i := ID{ID: "", Name: ""}
	Debug.Printf("jsonCommand is %s\n", string(jsonCommand))
	json.Unmarshal(jsonCommand, &c)

	//TODO:  Try to come up with something that makes me feel less dirty
	if c.Method == "POST" && strings.Contains(c.URL, "/channels/") && strings.Count(c.URL, "/") == 2 {
		chanID := strings.TrimPrefix(c.URL, "/channels/")
		if chanID != "" {
			p.addObject(chanID)
		}
	}
	//ENDTODO

	fullURL := strings.Join([]string{config.StasisURL, c.URL, "?api_key=", config.WSUser, ":", config.WSPassword}, "")

	Debug.Printf("fullURL is %s\n", fullURL)
	req, err := http.NewRequest(c.Method, fullURL, bytes.NewBufferString(c.Body))
	req.Header.Set("Content-Type", "application/json")
	res, err := client.Do(req)
	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Body)
	Debug.Printf("Response body is %s\n", buf.String())
	json.Unmarshal(buf.Bytes(), &i)
	if i.ID != "" {
		p.addObject(i.ID)
	} else if i.Name != "" {
		p.addObject(i.Name)
	}
	r.ResponseBody = buf.String()
	r.StatusCode = res.StatusCode
	r.UniqueID = c.UniqueID // return the Command UID in the response
	sendJSON, err := json.Marshal(r)
	if err != nil {
		Error.Println(err)
	}
	Debug.Printf("sendJSON is %s\n", string(sendJSON))
	responseProducer <- sendJSON
}

// vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab tw=72
<commit_msg>Update the dependencies to not mention google code<commit_after>// go-ari-proxy is an implementation of the go-ari-library used to
// connect to the Asterisk REST Interface for delivery of Events and
// Commands across a message bus.
package main

import (
	"bytes"
	"golang.org/x/net/websocket"
	"encoding/json"
	"flag"
	"github.com/nvisibleinc/go-ari-library"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// Var config contains a Config struct to hold the proxy configuration file.
var (
	config         Config            // main proxy configuration structure
	client         = &http.Client{}  // connection for Commands to ARI
	proxyInstances *proxyInstanceMap // maps the per-dialog proxy instances
	Debug          *log.Logger
	Info           *log.Logger
	Warning        *log.Logger
	Error          *log.Logger
)

// signalCatcher is a function to allows us to stop the application through an
// operating system signal.
func signalCatcher() {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	sig := <-ch
	log.Printf("Signal received: %v", sig)
	os.Exit(0)
}

// Init parses the configuration file by unmarshaling it into a Config struct.
func init() {
	var err error

	// Setup our logging interfaces
	Debug = ari.InitLogger(os.Stdout, "DEBUG")
	Info = ari.InitLogger(os.Stdout, "INFO")
	Warning = ari.InitLogger(os.Stdout, "WARNING")
	Error = ari.InitLogger(os.Stderr, "ERROR")

	// parse the configuration file and get data from it
	Info.Println("Loading configuration for proxy.")
	configpath := flag.String("config", "./config.json", "Path to config file")

	Debug.Println("Parsing the configuration file.")
	flag.Parse()
	Debug.Println("Reading in the configuration from the file.")
	configfile, err := ioutil.ReadFile(*configpath)
	if err != nil {
		Error.Fatal(err)
	}
	// read in the configuration file and unmarshal the json, storing it in 'config'
	Debug.Println("Unmarshaling proxy configuration.")
	json.Unmarshal(configfile, &config)
	Debug.Println(&config)
	Debug.Println("Initialize the proxy instance map.")
	proxyInstances = NewproxyInstanceMap() // initialize a new proxy instance map
}

func main() {
	// Setup a new Event producer and Command consumer for every application
	// we've configured in the configuration file.
	Info.Println("Initializing the message bus.")
	ari.InitBus(config.MessageBus, config.BusConfig)
	for _, app := range config.Applications {
		/*
			Create a new producer which is responsible for the initial topic on the message bus which is used
			to signal the setup of per-application instances. All applications listen to this topic in order to
			be provided the information to setup the ownership of per dialog application instances.
		*/
		Info.Printf("Initializing signalling bus for application %s", app)
		producer := ari.InitProducer(app) // Initialize a new producer channel using the ari.InitProducer function.
		Info.Printf("Starting event handler for application %s", app)
		go runEventHandler(app, producer) // create new websocket connection for every application and pass the producer channel
	}

	go signalCatcher() // listen for os signal to stop the application
	select {}
}

// runEventHandler sets up a websocket connection to an ARI application.
func runEventHandler(s string, producer chan []byte) {
	// Connect to the websocket backend (ARI)
	var ariMessage string
	url := strings.Join([]string{config.WebsocketURL, "?app=", s, "&api_key=", config.WSUser, ":", config.WSPassword}, "")

	Info.Printf("Attempting to connect to ARI websocket at: %s", url)
	ws, err := websocket.Dial(url, "ari", config.Origin)
	if err != nil {
		log.Fatal(err)
	}

	// Start the producer loop. Every message received from the websocket is
	// passed to the PublishMessage() function.
	Info.Printf("Starting producer loop for application %s", s)
	for {
		err = websocket.Message.Receive(ws, &ariMessage) // accept the message from the websocket
		if err != nil {
			log.Fatal(err)
		}
		go PublishMessage(ariMessage, producer) // publish message to the producer channel
	}
}

// PublishMessage takes an ARI event from the websocket and places it on the
// producer channel.
// Accepts two arguments:
// * a string containing the ARI message
// * a producer channel
func PublishMessage(ariMessage string, producer chan []byte) {
	// unmarshal into an ari.Event so we can append some extra information
	var info eventInfo
	var message ari.Event
	var pi *proxyInstance
	var exists bool = false
	json.Unmarshal([]byte(ariMessage), &message)
	json.Unmarshal([]byte(ariMessage), &info)
	message.ServerID = config.ServerID
	message.Timestamp = time.Now()
	message.ARI_Body = ariMessage

	switch {
	case info.Type == "StasisStart":

		// Check to see if the new channel was already in the map, which means it
		// was created by an originate with ID
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			break
		}
		// since we're starting a new application instance, create the proxy side
		dialogID := ari.UUID()
		Info.Println("New StasisStart found. Created new dialogID of ", dialogID)
		as, err := json.Marshal(ari.AppStart{Application: info.Application, DialogID: dialogID, ServerID: config.ServerID})
		producer <- as

		// TODO: this sleep is required to allow the application time to spin up. In the future we likely want
		// to implement some sort of feedback mechanism in order to remove this sleep timer.
		time.Sleep(50 * time.Millisecond)
		if err != nil {
			return
		}

		Info.Printf("Created new proxy instance mapping for dialog '%s' and channel '%s'", dialogID, info.Channel.ID)
		pi = NewProxyInstance(dialogID)         // create new proxy instance for the dialog
		proxyInstances.Add(info.Channel.ID, pi) // add the dialog to the proxyInstances map to track its life
		exists = true

	case info.Type == "StasisEnd":
		Info.Printf("Ending application instance for channel '%s'", info.Channel.ID)
		// on application end, perform clean up checks
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeAllObjects()
		}

	case info.Type == "BridgeDestroyed":
		pi, exists = proxyInstances.Get(info.Bridge.ID)
		if exists {
			pi.removeObject(info.Bridge.ID)
		}

	case info.Type == "ChannelDestroyed":
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeObject(info.Channel.ID)
		}

	// check if prefix is part of the minChan{} struct
	case strings.HasPrefix(info.Type, "Channel"):
		pi, exists = proxyInstances.Get(info.Channel.ID)

	// check if prefix is part of the minBridge{} struct
	case strings.HasPrefix(message.Type, "Bridge"):
		pi, exists = proxyInstances.Get(info.Bridge.ID)

	// check if prefix is part of the minPlay{} struct
	case strings.HasPrefix(message.Type, "Playback"):
		pi, exists = proxyInstances.Get(info.Playback.ID)

	// check if prefix is part of the minRec{} struct (this one uses a Name instead of ID for some reason)
	case strings.HasPrefix(message.Type, "Recording"):
		pi, exists = proxyInstances.Get(info.Recording.Name)

	default:
		Warning.Println("No handler for event type")
		//pi, exists = proxyInstances[]
		// if not matching, then we need to perform checks against the
		// existing map to determine where to send this ARI message.
	}

	// marshal the message back into a string
	busMessage, err := json.Marshal(message)
	if err != nil {
		Error.Println(err)
		return
	}
	Debug.Printf("Bus Data:\n%s\n", busMessage)

	// push the busMessage onto the producer channel
	if exists {
		pi.Events <- busMessage
	}
}

// Add inserts a proxy instance into the global map of active proxy instances.
func (p *proxyInstanceMap) Add(id string, pi *proxyInstance) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	p.instanceMap[id] = pi
}

// Get returns a proxy instance from the global map of active proxy instances.
// returns nil if not found.
func (p *proxyInstanceMap) Get(id string) (*proxyInstance, bool) {
	p.mapLock.RLock()
	defer p.mapLock.RUnlock()
	pi, ok := p.instanceMap[id]
	if !ok {
		return nil, false
	}
	return pi, true
}

// Remove deletes an entry in the global proxyInstanceMap
func (p *proxyInstanceMap) Remove(id string) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	delete(p.instanceMap, id)
}

// shutDown closes the quit channel to signal all of a ProxyInstance's goroutines
// to return
func (p *proxyInstance) shutDown() {
	select {
	case _, ok := (<-p.quit):
		if !ok {
			return
		}
	default:
		close(p.quit)
	}
}

// addObject adds an object reference to the proxyInstance mapping
func (p *proxyInstance) addObject(id string) {
	for i := range p.ariObjects {
		if p.ariObjects[i] == id {
			//object already is associated with this proxyInstance
			return
		}
	}
	p.ariObjects = append(p.ariObjects, id)
	proxyInstances.Add(id, p)

}

// removeObject removes an object reference from the proxyInstance mapping
func (p *proxyInstance) removeObject(id string) {
	// remove an object from the map.
	for i := range p.ariObjects {
		if p.ariObjects[i] == id {
			// rewrite the p.ariObjects string slice to append all values up to
			// the index value of 'i', and all values of 'i'+1 and later.
			p.ariObjects = append(p.ariObjects[:i], p.ariObjects[i+1:]...)
		}
	}
	// remove the instance from our tracking map
	proxyInstances.Remove(id)

	// if there are no more objects, shut'rdown
	if len(p.ariObjects) == 0 {
		p.shutDown()
	}
}

// removeAllObjects will remove all object references from the proxyInstance mapping
func (p *proxyInstance) removeAllObjects() {
	// remove all objects from the map as our application is shutting down.
	for _, obj := range p.ariObjects {
		proxyInstances.Remove(obj)
	}
	p.shutDown() // destroy the application / proxy instance
}

// runCommandConsumer starts the consumer for accepting Commands from
// applications.
func (p *proxyInstance) runCommandConsumer(dialogID string) {
	commandTopic := strings.Join([]string{"commands", dialogID}, "_")
	responseTopic := strings.Join([]string{"responses", dialogID}, "_")
	Debug.Println("Topics are:", commandTopic, " ", responseTopic)
	p.responseChannel = ari.InitProducer(responseTopic)

	// waits for the TopicExists function to return a channel
	select {
	case <-ari.TopicExists(commandTopic):
		p.commandChannel = ari.InitConsumer(commandTopic)
	case <-time.After(10 * time.Second):
		// if the application instance hasn't come up after a period of time, gracefully end the proxy instance
		p.removeAllObjects()
		return
	}

	for {
		select {
		case jsonCommand := <-p.commandChannel:
			go p.processCommand(jsonCommand, p.responseChannel)
		case <-p.quit:
			return
		}
	}
}

// processCommand processes commands from applications and submits them to the
// REST interface.
func (p *proxyInstance) processCommand(jsonCommand []byte, responseProducer chan []byte) {
	var c ari.Command
	var r ari.CommandResponse
	i := ID{ID: "", Name: ""}
	Debug.Printf("jsonCommand is %s\n", string(jsonCommand))
	json.Unmarshal(jsonCommand, &c)

	//TODO:  Try to come up with something that makes me feel less dirty
	if c.Method == "POST" && strings.Contains(c.URL, "/channels/") && strings.Count(c.URL, "/") == 2 {
		chanID := strings.TrimPrefix(c.URL, "/channels/")
		if chanID != "" {
			p.addObject(chanID)
		}
	}
	//ENDTODO

	fullURL := strings.Join([]string{config.StasisURL, c.URL, "?api_key=", config.WSUser, ":", config.WSPassword}, "")

	Debug.Printf("fullURL is %s\n", fullURL)
	req, err := http.NewRequest(c.Method, fullURL, bytes.NewBufferString(c.Body))
	req.Header.Set("Content-Type", "application/json")
	res, err := client.Do(req)
	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Body)
	Debug.Printf("Response body is %s\n", buf.String())
	json.Unmarshal(buf.Bytes(), &i)
	if i.ID != "" {
		p.addObject(i.ID)
	} else if i.Name != "" {
		p.addObject(i.Name)
	}
	r.ResponseBody = buf.String()
	r.StatusCode = res.StatusCode
	r.UniqueID = c.UniqueID // return the Command UID in the response
	sendJSON, err := json.Marshal(r)
	if err != nil {
		Error.Println(err)
	}
	Debug.Printf("sendJSON is %s\n", string(sendJSON))
	responseProducer <- sendJSON
}

// vim: tabstop=4 softtabstop=4 shiftwidth=4 noexpandtab tw=72
<|endoftext|>


<commit_before>// Package i3barjson provides a Go library for i3bar JSON protocol support.
package i3barjson

import (
	"encoding/json"
	"fmt"
	"io"
)

// Header represents the header of an i3bar message.
type Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events,omitempty"`
}

// String pretty prints Header objects.
func (d *Header) String() string {
	str, err := json.MarshalIndent(d, "", "    ")
	if err != nil {
		return err.Error()
	}
	return string(str)
}

// Block represents a single block of an i3bar message.
type Block struct {
	FullText            string `json:"full_text"`
	ShortText           string `json:"short_text,omitempty"`
	Color               string `json:"color,omitempty"`
	MinWidth            string `json:"min_width,omitempty"`
	Align               string `json:"align,omitempty"`
	Name                string `json:"name,omitempty"`
	Instance            string `json:"instance,omitempty"`
	Urgent              bool   `json:"urgent,omitempty"`
	Separator           bool   `json:"separator"`
	SeparatorBlockWidth int    `json:"separator_block_width,omitempty"`
	Markup              string `json:"markup,omitempty"`
}

// String pretty prints Block objects.
func (d *Block) String() string {
	str, err := json.MarshalIndent(d, "", "    ")
	if err != nil {
		return err.Error()
	}
	return string(str)
}

// StatusLine represents a full i3bar status line.
type StatusLine []*Block

// String pretty prints StatusLine objects.
func (d *StatusLine) String() string {
	str, err := json.MarshalIndent(d, "", "    ")
	if err != nil {
		return err.Error()
	}
	return string(str)
}

// Click represents an i3bar click event.
type Click struct {
	Name     string `json:"name"`
	Instance string `json:"instance"`
	X        int    `json:"x"`
	Y        int    `json:"y"`
	Button   int    `json:"button"`
}

// String pretty prints Click objects.
func (d *Click) String() string {
	str, err := json.MarshalIndent(d, "", "    ")
	if err != nil {
		return err.Error()
	}
	return string(str)
}

// jsonArrayEncoder is an object that streams an infinite JSON array.
type jsonArrayEncoder struct {
	count int
	w     io.Writer
	e     json.Encoder
}

// Encode streams an infinite JSON array.
// Each call adds another element to the array.
func (e *jsonArrayEncoder) Encode(v interface{}) error {
	linePrefix := ","
	if e.count == 0 {
		linePrefix = "["
		e.count++
	}
	_, err := e.w.Write([]byte(linePrefix))
	if err != nil {
		return err
	}

	err = e.e.Encode(v)
	if err != nil {
		return err
	}

	return nil
}

// newJsonArrayEncoder returns a new jsonArrayEncoder that wraps w.
func newJsonArrayEncoder(w io.Writer) *jsonArrayEncoder {
	return &jsonArrayEncoder{0, w, *json.NewEncoder(w)}
}

// Init initializes the i3bar io.
// w is the io.Writer to write to (usually os.Stdout).
// r is the io.Reader to from (usually os.Stdin) (TODO: implement).
// q is a channel that will be closed when the write loop is finished.
// The returned channel can be used to write status lines to w.
func Init(h *Header, w io.Writer, r io.Reader, q chan bool) (chan StatusLine, error) {
	if w == nil {
		return nil, fmt.Errorf("error: Writer required")
	}
	var jsonWriter = newJsonArrayEncoder(w)
	// TODO: implement read loop
	//var jsonReader *json.Decoder
	if r != nil {
		//jsonReader = json.NewDecoder(r)
	}

	msg, err := json.Marshal(h)
	if err != nil {
		return nil, fmt.Errorf("error: couldn't parse Header")
	}
	_, err = fmt.Fprintln(w, string(msg))
	if err != nil {
		return nil, fmt.Errorf("error: couldn't write to Writer")
	}

	statusChan := make(chan StatusLine)
	go writeLoop(jsonWriter, statusChan, q)
	return statusChan, nil
}

// writeLoop continuosly writes status lines sent over c to e.
// q is closed when there are no more values to read from c.
func writeLoop(e *jsonArrayEncoder, c chan StatusLine, q chan bool) {
	for block := range c {
		// TODO: proper error handling
		err := e.Encode(block)
		if err != nil {
			fmt.Printf("%s\n", err)
		}
	}

	close(q)
}
<commit_msg>centralized json marshalling functionality<commit_after>// Package i3barjson provides a Go library for i3bar JSON protocol support.
package i3barjson

import (
	"encoding/json"
	"fmt"
	"io"
)

// marshalIndent returns a marshalled JSON string of the given object.
func marshalIndent(d interface{}) string {
	str, err := json.MarshalIndent(d, "", "    ")
	if err != nil {
		return err.Error()
	}
	return string(str)
}

// Header represents the header of an i3bar message.
type Header struct {
	Version     int  `json:"version"`
	StopSignal  int  `json:"stop_signal,omitempty"`
	ContSignal  int  `json:"cont_signal,omitempty"`
	ClickEvents bool `json:"click_events,omitempty"`
}

// String pretty prints Header objects.
func (d *Header) String() string {
	return marshalIndent(d)
}

// Block represents a single block of an i3bar message.
type Block struct {
	FullText            string `json:"full_text"`
	ShortText           string `json:"short_text,omitempty"`
	Color               string `json:"color,omitempty"`
	MinWidth            string `json:"min_width,omitempty"`
	Align               string `json:"align,omitempty"`
	Name                string `json:"name,omitempty"`
	Instance            string `json:"instance,omitempty"`
	Urgent              bool   `json:"urgent,omitempty"`
	Separator           bool   `json:"separator"`
	SeparatorBlockWidth int    `json:"separator_block_width,omitempty"`
	Markup              string `json:"markup,omitempty"`
}

// String pretty prints Block objects.
func (d *Block) String() string {
	return marshalIndent(d)
}

// StatusLine represents a full i3bar status line.
type StatusLine []*Block

// String pretty prints StatusLine objects.
func (d *StatusLine) String() string {
	return marshalIndent(d)
}

// Click represents an i3bar click event.
type Click struct {
	Name     string `json:"name"`
	Instance string `json:"instance"`
	X        int    `json:"x"`
	Y        int    `json:"y"`
	Button   int    `json:"button"`
}

// String pretty prints Click objects.
func (d *Click) String() string {
	return marshalIndent(d)
}

// jsonArrayEncoder is an object that streams an infinite JSON array.
type jsonArrayEncoder struct {
	count int
	w     io.Writer
	e     json.Encoder
}

// Encode streams an infinite JSON array.
// Each call adds another element to the array.
func (e *jsonArrayEncoder) Encode(v interface{}) error {
	linePrefix := ","
	if e.count == 0 {
		linePrefix = "["
		e.count++
	}
	_, err := e.w.Write([]byte(linePrefix))
	if err != nil {
		return err
	}

	err = e.e.Encode(v)
	if err != nil {
		return err
	}

	return nil
}

// newJsonArrayEncoder returns a new jsonArrayEncoder that wraps w.
func newJsonArrayEncoder(w io.Writer) *jsonArrayEncoder {
	return &jsonArrayEncoder{0, w, *json.NewEncoder(w)}
}

// Init initializes the i3bar io.
// w is the io.Writer to write to (usually os.Stdout).
// r is the io.Reader to from (usually os.Stdin) (TODO: implement).
// q is a channel that will be closed when the write loop is finished.
// The returned channel can be used to write status lines to w.
func Init(h *Header, w io.Writer, r io.Reader, q chan bool) (chan StatusLine, error) {
	if w == nil {
		return nil, fmt.Errorf("error: Writer required")
	}
	var jsonWriter = newJsonArrayEncoder(w)
	// TODO: implement read loop
	//var jsonReader *json.Decoder
	if r != nil {
		//jsonReader = json.NewDecoder(r)
	}

	msg, err := json.Marshal(h)
	if err != nil {
		return nil, fmt.Errorf("error: couldn't parse Header")
	}
	_, err = fmt.Fprintln(w, string(msg))
	if err != nil {
		return nil, fmt.Errorf("error: couldn't write to Writer")
	}

	statusChan := make(chan StatusLine)
	go writeLoop(jsonWriter, statusChan, q)
	return statusChan, nil
}

// writeLoop continuosly writes status lines sent over c to e.
// q is closed when there are no more values to read from c.
func writeLoop(e *jsonArrayEncoder, c chan StatusLine, q chan bool) {
	for block := range c {
		// TODO: proper error handling
		err := e.Encode(block)
		if err != nil {
			fmt.Printf("%s\n", err)
		}
	}

	close(q)
}
<|endoftext|>


<commit_before>package godless

import (
	"testing"
)

func TestGodlessQueries(t *testing.T) {
	t.FailNow()
}
<commit_msg>Do whole system functional tests later.<commit_after><|endoftext|>


<commit_before>package gointerfacer

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"path/filepath"
	"strconv"
	"strings"

	"code.google.com/p/go.tools/imports"
)

// FindInterface returns the import path and identifier of an interface.
// For example, given "http.ResponseWriter", FindInterface returns
// "net/http", "ResponseWriter".
func FindInterface(iface string) (path string, pkg string, id string, err error) {
	if len(strings.Fields(iface)) != 1 {
		return "", "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	// Let goimports do the heavy lifting.
	src := []byte("package hack\n" + "var i " + iface)
	imp, err := imports.Process("", src, nil)
	if err != nil {
		return "", "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	// imp should now contain an appropriate import.
	// Parse out the import and the identifier.
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "", imp, 0)
	if err != nil {
		panic(err)
	}
	if len(f.Imports) == 0 {
		return "", "", "", fmt.Errorf("unrecognized interface: %s", iface)
	}
	raw := f.Imports[0].Path.Value   // "io"
	path, err = strconv.Unquote(raw) // io
	if err != nil {
		panic(err)
	}
	decl := f.Decls[1].(*ast.GenDecl)      // var i io.Reader
	spec := decl.Specs[0].(*ast.ValueSpec) // i io.Reader
	sel := spec.Type.(*ast.SelectorExpr)   // io.Reader
	pkg = sel.X.(*ast.Ident).Name          // io
	id = sel.Sel.Name                      // Reader
	return path, pkg, id, nil
}

// Pkg is a parsed build.Package.
type Pkg struct {
	*build.Package
	*token.FileSet
}

// TypeSpec locates the *ast.TypeSpec for type id in the import path.
func TypeSpec(path string, id string) (Pkg, *ast.TypeSpec, error) {
	pkg, err := build.Import(path, "", 0)
	if err != nil {
		return Pkg{}, nil, fmt.Errorf("couldn't find package %s: %v", path, err)
	}

	fset := token.NewFileSet() // share one fset across the whole package
	for _, file := range pkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, file), nil, 0)
		if err != nil {
			continue
		}

		for _, decl := range f.Decls {
			decl, ok := decl.(*ast.GenDecl)
			if !ok || decl.Tok != token.TYPE {
				continue
			}
			for _, spec := range decl.Specs {
				spec := spec.(*ast.TypeSpec)
				if spec.Name.Name != id {
					continue
				}
				return Pkg{Package: pkg, FileSet: fset}, spec, nil
			}
		}
	}
	return Pkg{}, nil, fmt.Errorf("type %s not found in %s", id, path)
}

// gofmt pretty-prints e.
func (p Pkg) gofmt(e ast.Expr) string {
	var buf bytes.Buffer
	printer.Fprint(&buf, p.FileSet, e)
	return buf.String()
}

// FullType returns the fully qualified type of e.
// Examples, assuming package net/http:
//  FullType(int) => "int"
//  FullType(Handler) => "http.Handler"
//  FullType(io.Reader) => "io.Reader"
//  FullType(*Request) => "*http.Request"
func (p Pkg) FullType(e ast.Expr) string {
	ast.Inspect(e, func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.Ident:
			// Using TypeSpec instead of IsExported here would be
			// more accurate, but it'd be crazy expensive, and if
			// the type isn't exported, there's no point trying
			// to implement it anyway.
			if n.IsExported() {
				n.Name = p.Package.Name + "." + n.Name
			}
		case *ast.SelectorExpr:
			return false
		}
		return true
	})
	return p.gofmt(e)
}

func (p Pkg) Params(field *ast.Field) []Param {
	var params []Param
	typ := p.FullType(field.Type)
	for _, name := range field.Names {
		params = append(params, Param{Name: name.Name, Type: typ})
	}
	// Handle anonymous params
	if len(params) == 0 {
		params = []Param{Param{Type: typ}}
	}
	return params
}

// Method represents a method signature.
type Method struct {
	Recv string
	Func
}

// Func represents a function signature.
type Func struct {
	Name   string
	Params []Param
	Res    []Param
}

// Param represents a parameter in a function or method signature.
type Param struct {
	Name string
	Type string
}

func (p Pkg) FuncSig(f *ast.Field) Func {
	fn := Func{Name: f.Names[0].Name}
	typ := f.Type.(*ast.FuncType)
	if typ.Params != nil {
		for _, field := range typ.Params.List {
			fn.Params = append(fn.Params, p.Params(field)...)
		}
	}
	if typ.Results != nil {
		for _, field := range typ.Results.List {
			fn.Res = append(fn.Res, p.Params(field)...)
		}
	}
	return fn
}

// Functions returns the set of methods required to implement iface.
// It is called Functions rather than methods because the
// function descriptions are functions; there is no receiver.
func Functions(iface string) ([]Func, error) {
	// Locate the interface.
	path, _, id, err := FindInterface(iface)
	if err != nil {
		return nil, err
	}

	// Parse the package and find the interface declaration.
	p, spec, err := TypeSpec(path, id)
	if err != nil {
		return nil, fmt.Errorf("interface %s not found: %s", iface, err)
	}
	idecl, ok := spec.Type.(*ast.InterfaceType)
	if !ok {
		return nil, fmt.Errorf("not an interface: %s", iface)
	}

	if idecl.Methods == nil {
		return nil, fmt.Errorf("empty interface: %s", iface)
	}

	var fns []Func
	for _, fndecl := range idecl.Methods.List {
		if len(fndecl.Names) == 0 {
			// Embedded interface: recurse
			embedded, err := Functions(p.FullType(fndecl.Type))
			if err != nil {
				return nil, err
			}
			fns = append(fns, embedded...)
			continue
		}

		fn := p.FuncSig(fndecl)
		fns = append(fns, fn)
	}
	return fns, nil
}
<commit_msg>Fixed go.tools/imports import<commit_after>package gointerfacer

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/build"
	"go/parser"
	"go/printer"
	"go/token"
	"path/filepath"
	"strconv"
	"strings"

	"golang.org/x/tools/imports"
)

// FindInterface returns the import path and identifier of an interface.
// For example, given "http.ResponseWriter", FindInterface returns
// "net/http", "ResponseWriter".
func FindInterface(iface string) (path string, pkg string, id string, err error) {
	if len(strings.Fields(iface)) != 1 {
		return "", "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	// Let goimports do the heavy lifting.
	src := []byte("package hack\n" + "var i " + iface)
	imp, err := imports.Process("", src, nil)
	if err != nil {
		return "", "", "", fmt.Errorf("couldn't parse interface: %s", iface)
	}

	// imp should now contain an appropriate import.
	// Parse out the import and the identifier.
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, "", imp, 0)
	if err != nil {
		panic(err)
	}
	if len(f.Imports) == 0 {
		return "", "", "", fmt.Errorf("unrecognized interface: %s", iface)
	}
	raw := f.Imports[0].Path.Value   // "io"
	path, err = strconv.Unquote(raw) // io
	if err != nil {
		panic(err)
	}
	decl := f.Decls[1].(*ast.GenDecl)      // var i io.Reader
	spec := decl.Specs[0].(*ast.ValueSpec) // i io.Reader
	sel := spec.Type.(*ast.SelectorExpr)   // io.Reader
	pkg = sel.X.(*ast.Ident).Name          // io
	id = sel.Sel.Name                      // Reader
	return path, pkg, id, nil
}

// Pkg is a parsed build.Package.
type Pkg struct {
	*build.Package
	*token.FileSet
}

// TypeSpec locates the *ast.TypeSpec for type id in the import path.
func TypeSpec(path string, id string) (Pkg, *ast.TypeSpec, error) {
	pkg, err := build.Import(path, "", 0)
	if err != nil {
		return Pkg{}, nil, fmt.Errorf("couldn't find package %s: %v", path, err)
	}

	fset := token.NewFileSet() // share one fset across the whole package
	for _, file := range pkg.GoFiles {
		f, err := parser.ParseFile(fset, filepath.Join(pkg.Dir, file), nil, 0)
		if err != nil {
			continue
		}

		for _, decl := range f.Decls {
			decl, ok := decl.(*ast.GenDecl)
			if !ok || decl.Tok != token.TYPE {
				continue
			}
			for _, spec := range decl.Specs {
				spec := spec.(*ast.TypeSpec)
				if spec.Name.Name != id {
					continue
				}
				return Pkg{Package: pkg, FileSet: fset}, spec, nil
			}
		}
	}
	return Pkg{}, nil, fmt.Errorf("type %s not found in %s", id, path)
}

// gofmt pretty-prints e.
func (p Pkg) gofmt(e ast.Expr) string {
	var buf bytes.Buffer
	printer.Fprint(&buf, p.FileSet, e)
	return buf.String()
}

// FullType returns the fully qualified type of e.
// Examples, assuming package net/http:
//  FullType(int) => "int"
//  FullType(Handler) => "http.Handler"
//  FullType(io.Reader) => "io.Reader"
//  FullType(*Request) => "*http.Request"
func (p Pkg) FullType(e ast.Expr) string {
	ast.Inspect(e, func(n ast.Node) bool {
		switch n := n.(type) {
		case *ast.Ident:
			// Using TypeSpec instead of IsExported here would be
			// more accurate, but it'd be crazy expensive, and if
			// the type isn't exported, there's no point trying
			// to implement it anyway.
			if n.IsExported() {
				n.Name = p.Package.Name + "." + n.Name
			}
		case *ast.SelectorExpr:
			return false
		}
		return true
	})
	return p.gofmt(e)
}

func (p Pkg) Params(field *ast.Field) []Param {
	var params []Param
	typ := p.FullType(field.Type)
	for _, name := range field.Names {
		params = append(params, Param{Name: name.Name, Type: typ})
	}
	// Handle anonymous params
	if len(params) == 0 {
		params = []Param{Param{Type: typ}}
	}
	return params
}

// Method represents a method signature.
type Method struct {
	Recv string
	Func
}

// Func represents a function signature.
type Func struct {
	Name   string
	Params []Param
	Res    []Param
}

// Param represents a parameter in a function or method signature.
type Param struct {
	Name string
	Type string
}

func (p Pkg) FuncSig(f *ast.Field) Func {
	fn := Func{Name: f.Names[0].Name}
	typ := f.Type.(*ast.FuncType)
	if typ.Params != nil {
		for _, field := range typ.Params.List {
			fn.Params = append(fn.Params, p.Params(field)...)
		}
	}
	if typ.Results != nil {
		for _, field := range typ.Results.List {
			fn.Res = append(fn.Res, p.Params(field)...)
		}
	}
	return fn
}

// Functions returns the set of methods required to implement iface.
// It is called Functions rather than methods because the
// function descriptions are functions; there is no receiver.
func Functions(iface string) ([]Func, error) {
	// Locate the interface.
	path, _, id, err := FindInterface(iface)
	if err != nil {
		return nil, err
	}

	// Parse the package and find the interface declaration.
	p, spec, err := TypeSpec(path, id)
	if err != nil {
		return nil, fmt.Errorf("interface %s not found: %s", iface, err)
	}
	idecl, ok := spec.Type.(*ast.InterfaceType)
	if !ok {
		return nil, fmt.Errorf("not an interface: %s", iface)
	}

	if idecl.Methods == nil {
		return nil, fmt.Errorf("empty interface: %s", iface)
	}

	var fns []Func
	for _, fndecl := range idecl.Methods.List {
		if len(fndecl.Names) == 0 {
			// Embedded interface: recurse
			embedded, err := Functions(p.FullType(fndecl.Type))
			if err != nil {
				return nil, err
			}
			fns = append(fns, embedded...)
			continue
		}

		fn := p.FuncSig(fndecl)
		fns = append(fns, fn)
	}
	return fns, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/csv"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/julienschmidt/httprouter"
)

const timeFormat = "2006-01-02"

type rateSlice []rate

func (rs rateSlice) Values() string {
	str := "["
	for i, r := range rs {
		str += "["
		str += strconv.FormatInt(r.Date.Unix()*1000, 10) // in milli seconds
		str += ","
		str += strconv.FormatFloat(r.Value, 'g', 4, 64)
		str += "]"
		if i != len(rs)-1 {
			str += ","
		}
	}
	str += "]"

	return str
}

type rate struct {
	Date  time.Time `json:"date"`
	Value float64   `json:"value"`
}

// MarshalJSON overrides marshal, prints shorter date string
func (r *rate) MarshalJSON() ([]byte, error) {
	type alias rate
	return json.Marshal(&struct {
		Date string `json:"date"`
		*alias
	}{
		Date:  r.Date.Format("2006-01-02"),
		alias: (*alias)(r),
	})
}

var lastRefresh time.Time
var maturities = []string{"1w", "2w", "1m", "2m", "3m", "6m", "9m", "12m"}
var retentions = []string{"week", "month", "three_months", "six_months", "year", "two_years", "six_years"}
var retentionMap = map[string]string{
	"last-week":       "week",
	"last-month":      "month",
	"last-quarter":    "three_months",
	"last-six-months": "six_months",
	"last-year":       "year",
	"last-two-years":  "two_years",
	"last-six-years":  "six_years",
}
var webRoot string
var historyPath string
var historyCache map[string][]rate
var influxCache map[string]map[string][]rate

// runs in go routine and takes care of refreshing the cache
func refreshCache() {
	historyCache = make(map[string][]rate)
	influxCache = make(map[string]map[string][]rate)

	for {
		if time.Since(lastRefresh) < time.Hour*24 {
			time.Sleep(time.Minute)
			continue
		}

		// refresh
		log.Println("refreshing history cache")
		for _, maturity := range maturities {
			// TODO: make path to files configureable
			file := fmt.Sprintf("%s/euribor-rates-%s.csv", historyPath, maturity)
			historyCache[maturity] = parseFile(file)
		}
		log.Println("refreshing influx cache")
		for _, retention := range retentions {
			results := queryInflux(retention)
			cache := make(map[string][]rate)
			// fmt.Println(results.Values)
			// fmt.Println(reflect.TypeOf(results.Values).Kind())
			for _, value := range results.Values {
				// fmt.Println(value, reflect.TypeOf(value).Kind(), reflect.TypeOf(value))
				m, r, err := transformInfluxValueToRate(reflect.ValueOf(value).Interface().([]interface{}))
				if err != nil {
					log.Println("error converting influx value to rate")
					continue
				}
				if rates, ok := cache[m]; ok {
					rates = append(rates, r)
					cache[m] = rates
				} else {
					cache[m] = []rate{r}
				}
			}
			influxCache[retention] = cache
		}
		log.Println("cache refresh completed")

		lastRefresh = time.Now()
	}
}

func parseFile(file string) []rate {
	f, err := os.Open(file)
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	r := csv.NewReader(f)
	r.Comment = '#'
	r.FieldsPerRecord = 2

	records, err := r.ReadAll()
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	rates := []rate{}
	for _, record := range records {
		date, err := time.ParseInLocation(timeFormat, record[0], time.UTC)
		if err != nil {
			log.Println(err)
			continue
		}
		value, err := strconv.ParseFloat(record[1], 64)
		if err != nil {
			log.Println(err)
			continue
		}
		rates = append(rates, rate{date, value})
	}

	return rates
}

func isValidRetention(r string) bool {
	if _, ok := retentionMap[r]; ok {
		return true
	}
	return false
}

func isValidMaturity(m string) bool {
	switch m {
	case "1w":
		return true
	case "2w":
		return true
	case "1m":
		return true
	case "2m":
		return true
	case "3m":
		return true
	case "6m":
		return true
	case "9m":
		return true
	case "12m":
		return true
	}
	return false
}

// index handler
func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	log.Printf("%v /", r.RemoteAddr)
	fmt.Fprint(w, "Welcome to the Euribor rates service!\n")
}

// influx handler
func influx(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /rates/app/%s/%s", r.RemoteAddr, params.ByName("retention"), params.ByName("maturity"))
	retention := params.ByName("retention")
	if isValidRetention(retention) == false {
		//TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown retention"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range influxCache[retentionMap[retention]][maturity] {
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

// highstock handler
func highstock(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /rates/app/hs/%s", r.RemoteAddr, params.ByName("maturity"))
	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := rateSlice{}
	for _, r := range historyCache[maturity] {
		rates = append(rates, r)
	}

	fmt.Fprintf(w, rates.Values())
}

// history handler
func history(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /rates/history/%s/%s", r.RemoteAddr, params.ByName("year"), params.ByName("maturity"))
	year, err := strconv.ParseInt(params.ByName("year"), 10, 32)
	if err != nil {
		// TODO: return http error code
		fmt.Fprint(w, errorMsg(err.Error()))
		return
	}
	if year < 2010 || year > int64(time.Now().Year()) {
		fmt.Fprintf(w, errorMsg("no data"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range historyCache[maturity] {
		if int64(r.Date.Year()) != year {
			continue
		}
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

// webapp handler
func webapp(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /webapp", r.RemoteAddr)

	fmt.Fprintf(w, renderWebapp(webRoot))
}

func errorMsg(msg string) string {
	return fmt.Sprintf("{\"error\":\"%s\"}", msg)
}

func main() {
	var host string
	var port string
	flag.StringVar(&host, "host", "localhost", "host to bind to")
	flag.StringVar(&port, "port", "8080", "port to bind to")
	flag.StringVar(&webRoot, "web-root", "", "root path if hosted behind a proxy")
	flag.StringVar(&historyPath, "history-path", ".", "path to history rate CSV files")
	flag.Parse()

	go refreshCache()

	router := httprouter.New()
	router.RedirectTrailingSlash = true

	// routes for general info
	// TODO: add routes for list of supported retentions/maturities
	router.GET("/", index)

	// routes to serve the app
	router.GET("/rates/app/if/:retention/:maturity", influx)
	router.GET("/rates/app/hs/:maturity", highstock)

	// routes to serve historical queries
	router.GET("/rates/history/:year/:maturity", history)

	// routes to serve the webapp
	router.GET("/webapp", webapp)

	log.Printf("listening on %s:%s", host, port)
	log.Fatal(http.ListenAndServe(host+":"+port, router))
}
<commit_msg>Endpoint and cmd flag to get version<commit_after>package main

import (
	"encoding/csv"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"reflect"
	"strconv"
	"time"

	"github.com/julienschmidt/httprouter"
)

const (
	appName    = "gorates"
	timeFormat = "2006-01-02"
)

var version string

type rateSlice []rate

func (rs rateSlice) Values() string {
	str := "["
	for i, r := range rs {
		str += "["
		str += strconv.FormatInt(r.Date.Unix()*1000, 10) // in milli seconds
		str += ","
		str += strconv.FormatFloat(r.Value, 'g', 4, 64)
		str += "]"
		if i != len(rs)-1 {
			str += ","
		}
	}
	str += "]"

	return str
}

type rate struct {
	Date  time.Time `json:"date"`
	Value float64   `json:"value"`
}

// MarshalJSON overrides marshal, prints shorter date string
func (r *rate) MarshalJSON() ([]byte, error) {
	type alias rate
	return json.Marshal(&struct {
		Date string `json:"date"`
		*alias
	}{
		Date:  r.Date.Format("2006-01-02"),
		alias: (*alias)(r),
	})
}

var lastRefresh time.Time
var maturities = []string{"1w", "2w", "1m", "2m", "3m", "6m", "9m", "12m"}
var retentions = []string{"week", "month", "three_months", "six_months", "year", "two_years", "six_years"}
var retentionMap = map[string]string{
	"last-week":       "week",
	"last-month":      "month",
	"last-quarter":    "three_months",
	"last-six-months": "six_months",
	"last-year":       "year",
	"last-two-years":  "two_years",
	"last-six-years":  "six_years",
}
var webRoot string
var historyPath string
var historyCache map[string][]rate
var influxCache map[string]map[string][]rate

// runs in go routine and takes care of refreshing the cache
func refreshCache() {
	historyCache = make(map[string][]rate)
	influxCache = make(map[string]map[string][]rate)

	for {
		if time.Since(lastRefresh) < time.Hour*24 {
			time.Sleep(time.Minute)
			continue
		}

		// refresh
		log.Println("refreshing history cache")
		for _, maturity := range maturities {
			// TODO: make path to files configureable
			file := fmt.Sprintf("%s/euribor-rates-%s.csv", historyPath, maturity)
			historyCache[maturity] = parseFile(file)
		}
		log.Println("refreshing influx cache")
		for _, retention := range retentions {
			results := queryInflux(retention)
			cache := make(map[string][]rate)
			// fmt.Println(results.Values)
			// fmt.Println(reflect.TypeOf(results.Values).Kind())
			for _, value := range results.Values {
				// fmt.Println(value, reflect.TypeOf(value).Kind(), reflect.TypeOf(value))
				m, r, err := transformInfluxValueToRate(reflect.ValueOf(value).Interface().([]interface{}))
				if err != nil {
					log.Println("error converting influx value to rate")
					continue
				}
				if rates, ok := cache[m]; ok {
					rates = append(rates, r)
					cache[m] = rates
				} else {
					cache[m] = []rate{r}
				}
			}
			influxCache[retention] = cache
		}
		log.Println("cache refresh completed")

		lastRefresh = time.Now()
	}
}

func parseFile(file string) []rate {
	f, err := os.Open(file)
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	r := csv.NewReader(f)
	r.Comment = '#'
	r.FieldsPerRecord = 2

	records, err := r.ReadAll()
	if err != nil {
		log.Println(err)
		return []rate{}
	}

	rates := []rate{}
	for _, record := range records {
		date, err := time.ParseInLocation(timeFormat, record[0], time.UTC)
		if err != nil {
			log.Println(err)
			continue
		}
		value, err := strconv.ParseFloat(record[1], 64)
		if err != nil {
			log.Println(err)
			continue
		}
		rates = append(rates, rate{date, value})
	}

	return rates
}

func isValidRetention(r string) bool {
	if _, ok := retentionMap[r]; ok {
		return true
	}
	return false
}

func isValidMaturity(m string) bool {
	switch m {
	case "1w":
		return true
	case "2w":
		return true
	case "1m":
		return true
	case "2m":
		return true
	case "3m":
		return true
	case "6m":
		return true
	case "9m":
		return true
	case "12m":
		return true
	}
	return false
}

// index handler
func index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	log.Printf("%v /", r.RemoteAddr)
	fmt.Fprint(w, "Welcome to the Euribor rates service!\n")
}

// version handler
func versionHandler(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	log.Printf("%v /version", r.RemoteAddr)
	fmt.Fprintf(w, "{\"version\":\"%s\"}", version)
}

// influx handler
func influx(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /rates/app/%s/%s", r.RemoteAddr, params.ByName("retention"), params.ByName("maturity"))
	retention := params.ByName("retention")
	if isValidRetention(retention) == false {
		//TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown retention"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range influxCache[retentionMap[retention]][maturity] {
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

// highstock handler
func highstock(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /rates/app/hs/%s", r.RemoteAddr, params.ByName("maturity"))
	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := rateSlice{}
	for _, r := range historyCache[maturity] {
		rates = append(rates, r)
	}

	fmt.Fprintf(w, rates.Values())
}

// history handler
func history(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /rates/history/%s/%s", r.RemoteAddr, params.ByName("year"), params.ByName("maturity"))
	year, err := strconv.ParseInt(params.ByName("year"), 10, 32)
	if err != nil {
		// TODO: return http error code
		fmt.Fprint(w, errorMsg(err.Error()))
		return
	}
	if year < 2010 || year > int64(time.Now().Year()) {
		fmt.Fprintf(w, errorMsg("no data"))
		return
	}

	maturity := params.ByName("maturity")
	if isValidMaturity(maturity) == false {
		// TODO: return http error code
		fmt.Fprintf(w, errorMsg("uknown maturity"))
		return
	}

	rates := []rate{}
	for _, r := range historyCache[maturity] {
		if int64(r.Date.Year()) != year {
			continue
		}
		rates = append(rates, r)
	}

	jsonData, err := json.Marshal(rates)
	if err != nil {
		fmt.Fprintf(w, errorMsg(err.Error()))
		return
	}

	fmt.Fprintf(w, string(jsonData))
}

// webapp handler
func webapp(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	log.Printf("%v /webapp", r.RemoteAddr)

	fmt.Fprintf(w, renderWebapp(webRoot))
}

func errorMsg(msg string) string {
	return fmt.Sprintf("{\"error\":\"%s\"}", msg)
}

func main() {
	var host string
	var port string
	var verFlag bool
	flag.StringVar(&host, "host", "localhost", "host to bind to")
	flag.StringVar(&port, "port", "8080", "port to bind to")
	flag.StringVar(&webRoot, "web-root", "", "root path if hosted behind a proxy")
	flag.StringVar(&historyPath, "history-path", ".", "path to history rate CSV files")
	flag.BoolVar(&verFlag, "version", false, "print version and exit")
	flag.Parse()

	if verFlag {
		fmt.Printf("gorates: version=%s\n", version)
		return
	}

	go refreshCache()

	router := httprouter.New()
	router.RedirectTrailingSlash = true

	// routes for general info
	// TODO: add routes for list of supported retentions/maturities
	router.GET("/", index)
	router.GET("/version", versionHandler)

	// routes to serve the app
	router.GET("/rates/app/if/:retention/:maturity", influx)
	router.GET("/rates/app/hs/:maturity", highstock)

	// routes to serve historical queries
	router.GET("/rates/history/:year/:maturity", history)

	// routes to serve the webapp
	router.GET("/webapp", webapp)

	log.Printf("listening on %s:%s", host, port)
	log.Fatal(http.ListenAndServe(host+":"+port, router))
}
<|endoftext|>


<commit_before>//  Copyright 2016 The goscope Authors
//
//  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 gui

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/scope"
)

func TestPlotToPng(t *testing.T) {
	dev, err := dummy.Open("")
	if err != nil {
		t.Fatalf("Cannot open the device: %v", err)
	}
	dir, err := ioutil.TempDir("", "TestPlotToPng")
	if err != nil {
		t.Fatalf("Cannot create temp dir: %v", err)
	}
	defer os.RemoveAll(dir)
	err = PlotToPng(dev, make(map[scope.ChanID]ZeroAndScale), filepath.Join(dir, "plot.png"))
	if err != nil {
		t.Fatalf("Cannot plot to file: %v", err)
	}
}

func TestPlotToPngWithCustomScales(t *testing.T) {
	dev, err := dummy.Open("")
	if err != nil {
		t.Fatalf("Cannot open the device: %v", err)
	}
	dir, err := ioutil.TempDir("", "TestPlotToPngWithCustomScales")
	if err != nil {
		t.Fatalf("Cannot create temp dir: %v", err)
	}
	defer os.RemoveAll(dir)
	zas := map[scope.ChanID]ZeroAndScale{
		"square":   ZeroAndScale{0.1, 5},
		"triangle": ZeroAndScale{0.8, 2},
	}
	err = PlotToPng(dev, zas, filepath.Join(dir, "plot.png"))
	if err != nil {
		t.Fatalf("Cannot plot to file: %v", err)
	}
}

func BenchmarkCreatePlot(b *testing.B) {
	dev, err := dummy.Open("")
	if err != nil {
		b.Fatalf("Cannot open the device: %v", err)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = CreatePlot(dev, make(map[scope.ChanID]ZeroAndScale))
		if err != nil {
			b.Fatalf("Cannot create plot: %v", err)
		}
	}
}
<commit_msg>TODOs added<commit_after>//  Copyright 2016 The goscope Authors
//
//  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 gui

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"github.com/zagrodzki/goscope/dummy"
	"github.com/zagrodzki/goscope/scope"
)

// TODO: more tests for specific functions
// TODO: tests comparing the resulting plot image to the perfect image

func TestPlotToPng(t *testing.T) {
	dev, err := dummy.Open("")
	if err != nil {
		t.Fatalf("Cannot open the device: %v", err)
	}
	dir, err := ioutil.TempDir("", "TestPlotToPng")
	if err != nil {
		t.Fatalf("Cannot create temp dir: %v", err)
	}
	defer os.RemoveAll(dir)
	err = PlotToPng(dev, make(map[scope.ChanID]ZeroAndScale), filepath.Join(dir, "plot.png"))
	if err != nil {
		t.Fatalf("Cannot plot to file: %v", err)
	}
}

func TestPlotToPngWithCustomScales(t *testing.T) {
	dev, err := dummy.Open("")
	if err != nil {
		t.Fatalf("Cannot open the device: %v", err)
	}
	dir, err := ioutil.TempDir("", "TestPlotToPngWithCustomScales")
	if err != nil {
		t.Fatalf("Cannot create temp dir: %v", err)
	}
	defer os.RemoveAll(dir)
	zas := map[scope.ChanID]ZeroAndScale{
		"square":   ZeroAndScale{0.1, 5},
		"triangle": ZeroAndScale{0.8, 2},
	}
	err = PlotToPng(dev, zas, filepath.Join(dir, "plot.png"))
	if err != nil {
		t.Fatalf("Cannot plot to file: %v", err)
	}
}

func BenchmarkCreatePlot(b *testing.B) {
	dev, err := dummy.Open("")
	if err != nil {
		b.Fatalf("Cannot open the device: %v", err)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err = CreatePlot(dev, make(map[scope.ChanID]ZeroAndScale))
		if err != nil {
			b.Fatalf("Cannot create plot: %v", err)
		}
	}
}
<|endoftext|>