<commit_before>package expr

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/raintank/metrictank/api/models"
	"github.com/raintank/metrictank/consolidation"
)

// here we use smartSummarize because it has multiple optional arguments which allows us to test some interesting things
func TestArgs(t *testing.T) {

	from := uint32(1000)
	to := uint32(2000)
	stable := true

	cases := []struct {
		name      string
		args      []*expr
		namedArgs map[string]*expr
		expReq    []Req
		expErr    error
	}{
		{
			"2 args normal, 0 optional",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by position",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by key",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 1 by position, 1 by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 by position, 1 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"alignToFrom"},
		},
		{
			"2 args normal, 1 by position, 2 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"func"},
		},
		{
			"2 args normal, 0 by position, the first by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func": {etype: etString, str: "sum"},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 0 by position, the second by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"missing required argument",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
			},
			nil,
			nil,
			ErrMissingArg,
		},
	}

	fn := NewSmartSummarize()
	for i, c := range cases {
		e := &expr{
			etype:     etFunc,
			str:       "smartSummarize",
			args:      c.args,
			namedArgs: c.namedArgs,
		}
		req, err := newplanFunc(e, fn, Context{from: from, to: to}, stable, nil)
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.name, c.expErr, err)
		}
		if !reflect.DeepEqual(req, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.name, c.expReq, req)
		}
	}
}

// TestConsolidateBy tests for a variety of input targets, wether consolidateBy settings are correctly
// propagated down the tree (to fetch requests) and up the tree (to runtime consolidation of the output)
func TestConsolidateBy(t *testing.T) {
	from := uint32(1000)
	to := uint32(2000)
	stable := true
	cases := []struct {
		in     string
		expReq []Req // to verify consolidation settings of fetch requests
		expErr error
		expOut []models.Series // we only use QueryPatt and Consolidator field just to check consolidation settings of output series
	}{
		{
			"a",
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: "a", Consolidator: consolidation.Avg},
			},
		},
		{
			// consolidation flows both up and down the tree
			`consolidateBy(a, "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(a,"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrap with regular function -> consolidation goes both up and down
			`scale(consolidateBy(a, "sum"),1)`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `scale(consolidateBy(a,"sum"),1.000000)`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrapping by a special function does not affect fetch consolidation, but resets output consolidation
			`perSecond(consolidateBy(a, "sum"))`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `perSecond(consolidateBy(a,"sum"))`, Consolidator: consolidation.None},
			},
		},
		{
			// consolidation setting streams down and up unaffected by scale
			`consolidateBy(scale(a, 1), "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(scale(a,1.000000),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// perSecond changes data semantics, fetch consolidation should be reset to default
			`consolidateBy(perSecond(a), "sum")`,
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(perSecond(a),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			// TODO: I think it can be argued that the max here is only intended for the output, not to the inputs
			`consolidateBy(divideSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(divideSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			`consolidateBy(sumSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(sumSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
	}

	for i, c := range cases {
		fmt.Println("#####", c.in)
		// for the purpose of this test, we assume ParseMany works fine.
		exprs, _ := ParseMany([]string{c.in})
		plan, err := NewPlan(exprs, from, to, 800, stable, nil)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.in, c.expErr, err)
		}
		if !reflect.DeepEqual(plan.Reqs, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.in, c.expReq, plan.Reqs)
		}
		input := map[Req][]models.Series{
			NewReq("a", from, to, 0): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Avg, // emulate the fact that a by default will use avg
			}},
			NewReq("a", from, to, consolidation.Min): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Min,
			}},
			NewReq("a", from, to, consolidation.Sum): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Sum,
			}},
			NewReq("b", from, to, consolidation.Max): {{
				QueryPatt:    "b",
				Consolidator: consolidation.Max,
			}},
		}
		out, err := plan.Run(input)
		if err != nil {
			t.Fatal(err)
		}
		if len(out) != len(c.expOut) {
			t.Errorf("case %d: %q, expected %d series output, not %d", i, c.in, len(c.expOut), len(out))
		}
		for j, exp := range c.expOut {
			if exp.QueryPatt != out[j].QueryPatt || exp.Consolidator != out[j].Consolidator {
				t.Errorf("case %d: %q, output series mismatch at pos %d: expected %v-%v - got %v-%v", i, c.in, j, exp.QueryPatt, exp.Consolidator, out[j].QueryPatt, out[j].Consolidator)
			}
		}
	}
}
<commit_msg>remove stale prints<commit_after>package expr

import (
	"reflect"
	"testing"

	"github.com/raintank/metrictank/api/models"
	"github.com/raintank/metrictank/consolidation"
)

// here we use smartSummarize because it has multiple optional arguments which allows us to test some interesting things
func TestArgs(t *testing.T) {

	from := uint32(1000)
	to := uint32(2000)
	stable := true

	cases := []struct {
		name      string
		args      []*expr
		namedArgs map[string]*expr
		expReq    []Req
		expErr    error
	}{
		{
			"2 args normal, 0 optional",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by position",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by key",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 1 by position, 1 by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 by position, 1 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"alignToFrom"},
		},
		{
			"2 args normal, 1 by position, 2 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"func"},
		},
		{
			"2 args normal, 0 by position, the first by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func": {etype: etString, str: "sum"},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 0 by position, the second by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"missing required argument",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
			},
			nil,
			nil,
			ErrMissingArg,
		},
	}

	fn := NewSmartSummarize()
	for i, c := range cases {
		e := &expr{
			etype:     etFunc,
			str:       "smartSummarize",
			args:      c.args,
			namedArgs: c.namedArgs,
		}
		req, err := newplanFunc(e, fn, Context{from: from, to: to}, stable, nil)
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.name, c.expErr, err)
		}
		if !reflect.DeepEqual(req, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.name, c.expReq, req)
		}
	}
}

// TestConsolidateBy tests for a variety of input targets, wether consolidateBy settings are correctly
// propagated down the tree (to fetch requests) and up the tree (to runtime consolidation of the output)
func TestConsolidateBy(t *testing.T) {
	from := uint32(1000)
	to := uint32(2000)
	stable := true
	cases := []struct {
		in     string
		expReq []Req // to verify consolidation settings of fetch requests
		expErr error
		expOut []models.Series // we only use QueryPatt and Consolidator field just to check consolidation settings of output series
	}{
		{
			"a",
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: "a", Consolidator: consolidation.Avg},
			},
		},
		{
			// consolidation flows both up and down the tree
			`consolidateBy(a, "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(a,"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrap with regular function -> consolidation goes both up and down
			`scale(consolidateBy(a, "sum"),1)`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `scale(consolidateBy(a,"sum"),1.000000)`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrapping by a special function does not affect fetch consolidation, but resets output consolidation
			`perSecond(consolidateBy(a, "sum"))`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `perSecond(consolidateBy(a,"sum"))`, Consolidator: consolidation.None},
			},
		},
		{
			// consolidation setting streams down and up unaffected by scale
			`consolidateBy(scale(a, 1), "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(scale(a,1.000000),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// perSecond changes data semantics, fetch consolidation should be reset to default
			`consolidateBy(perSecond(a), "sum")`,
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(perSecond(a),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			// TODO: I think it can be argued that the max here is only intended for the output, not to the inputs
			`consolidateBy(divideSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(divideSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			`consolidateBy(sumSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(sumSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
	}

	for i, c := range cases {
		// for the purpose of this test, we assume ParseMany works fine.
		exprs, _ := ParseMany([]string{c.in})
		plan, err := NewPlan(exprs, from, to, 800, stable, nil)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.in, c.expErr, err)
		}
		if !reflect.DeepEqual(plan.Reqs, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.in, c.expReq, plan.Reqs)
		}
		input := map[Req][]models.Series{
			NewReq("a", from, to, 0): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Avg, // emulate the fact that a by default will use avg
			}},
			NewReq("a", from, to, consolidation.Min): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Min,
			}},
			NewReq("a", from, to, consolidation.Sum): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Sum,
			}},
			NewReq("b", from, to, consolidation.Max): {{
				QueryPatt:    "b",
				Consolidator: consolidation.Max,
			}},
		}
		out, err := plan.Run(input)
		if err != nil {
			t.Fatal(err)
		}
		if len(out) != len(c.expOut) {
			t.Errorf("case %d: %q, expected %d series output, not %d", i, c.in, len(c.expOut), len(out))
		}
		for j, exp := range c.expOut {
			if exp.QueryPatt != out[j].QueryPatt || exp.Consolidator != out[j].Consolidator {
				t.Errorf("case %d: %q, output series mismatch at pos %d: expected %v-%v - got %v-%v", i, c.in, j, exp.QueryPatt, exp.Consolidator, out[j].QueryPatt, out[j].Consolidator)
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"syscall"
	"bytes"
	"encoding/binary"
	"flag"
	"log"
	"os"
	"os/exec"
	"io/ioutil"
	"strings"
	"time"
)

const (
	EVENT_SIZE = 16
)

type inotifyEvent struct {
	wd int32
	mask int32
	cookie int32
	length int32
	name string
}

type polledFile struct {
	path string
	modTime time.Time
}

var (
	path string
	command string
	ext string
	pid int
	polling bool
	lastEvent *inotifyEvent
	pollList map[string] polledFile
	ignoreDir map[string]bool
)

func init() {
	flag.StringVar(&path, "watch", ".", "path to be watched")
	flag.StringVar(&command, "command", "echo", "path to be watched")
	flag.StringVar(&ext, "ext", "go", "extension to be watched")
	flag.BoolVar(&polling, "polling", false, "use polling")
	flag.BoolVar(&polling, "p", false, "use polling")
	flag.Parse()
	ignoreDir := make(map[string]bool,256)
	ignoreDir[".git"] = true
}

func intFromByte(byteSlice []byte, data interface{} ) {
	err := binary.Read(bytes.NewBuffer(byteSlice), binary.LittleEndian, data)
	if err != nil {
		log.Fatal("binary.read failed: ", err)
	}
}

func runApp() {
	log.Print("Starting Process...")
	commandArray := strings.Split(command, " ")
	paramArray := commandArray[1:]
	cmd := exec.Command(commandArray[0], paramArray...)
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Process Started Successfuly: ",cmd.Process.Pid)
	pid = cmd.Process.Pid
}

func processBuffer(n int, buffer []byte) {
	event := new(inotifyEvent)
	defer func() { lastEvent = event }()
	var  i int32

	for i < int32(n) {
		intFromByte(buffer[i: i + 4], &event.wd)
		intFromByte(buffer[i+ 4: i + 8], &event.mask)
		intFromByte(buffer[i + 8: i + 12], &event.cookie)
		intFromByte(buffer[i + 12: i + 16], &event.length)
		event.name =string(buffer[i + 16: i + 16 + event.length])
		event.name = strings.TrimRight(event.name, "\x00")
		i += EVENT_SIZE + event.length

//		log.Print(event)
//		continue

		if(len(strings.Split(event.name,".")) > 1) {
			eventExt := strings.Split(event.name,".")[1]
			if(ext == eventExt){
				if lastEvent != nil && lastEvent.name == event.name && lastEvent.mask == 0x100 && event.mask == 0x2 {
					log.Print("Skipping as we already processed events for file: ", event.name)
					break
				}
				log.Print("Killing Process:  ",pid)
				if proc, err := os.FindProcess(pid); err != nil {
					log.Print("error: ",err)
					runApp()
				}else{
					err := proc.Kill()
					if err != nil {
						log.Print("error: ", err)
					}
					_, err = proc.Wait()
					if err != nil {
						log.Print("error: ", err)
					}
					runApp()
				}
				break
			}
		}
	}
}

func runInotify() {
	fd, err := syscall.InotifyInit()
	if err != nil {
		log.Fatal("error initializing Inotify: ", err)
		return
	}
	addFilesToInotify(fd, path)

	var buffer []byte = make([]byte, 1024 * EVENT_SIZE)

	for {
		n, err := syscall.Read(fd, buffer)
		if err != nil {
			log.Fatal("Read failed: ", err)
			return
		}
		processBuffer(n, buffer)
	}
}

func addFilesToInotify(fd int, dirPath string) {
	dir, err := os.Stat(dirPath)
	if err != nil {
		log.Fatal("error getting info on dir: ", err)
		return
	}
	if dir.IsDir() && dir.Name() != ".git" {
		log.Print("adding: ", dirPath)
		_, err = syscall.InotifyAddWatch(fd, dirPath, syscall.IN_CLOSE_WRITE | syscall.IN_DELETE)
		if err != nil {
			log.Fatal("error adding watch: ", err)
			return
		}

		fileList, err := ioutil.ReadDir(dirPath)
		if err != nil {
			log.Fatal("error reading dir: ", err)
			return
		}
		for _,file := range fileList {
			newPath := dirPath + "/" + file.Name()
			if file.IsDir() && file.Name() != ".git" {
				addFilesToInotify(fd, newPath)
			}
		}
	}
}

func addFilesToPoll(filePath string) {
		fileList, err := ioutil.ReadDir(filePath)
		if err != nil {
			log.Fatal("ReadDir failed: ", err)
		}
		for _,file := range fileList {
			newPath := filePath + "/" + file.Name()
			if file.IsDir() && file.Name() != ".git" {
				pollList[newPath] = polledFile{path:newPath, modTime:file.ModTime()}
				addFilesToPoll(newPath)
			} else {
				fileName := file.Name()
				if(len(strings.Split(fileName,".")) > 1) {
					fileExt := strings.Split(fileName,".")[1]
					if(fileExt == ext){
						pollList[newPath] = polledFile{path:newPath, modTime:file.ModTime()}
						log.Print(fileName, " - ", file.ModTime())
					}
				}
			}
		}
}

func runPolling() {
	pollList = make(map[string] polledFile)
	addFilesToPoll(path)
	for{
		for file,modTime := range pollList {
			fileInfo, err := os.Stat(file)
			if err != nil {
				log.Fatal("Stat error: ", err)
			}
			pollList[file] = polledFile{path: file, modTime:fileInfo.ModTime()}
			log.Print(file, " - ",  modTime)
		}
		time.Sleep(500*time.Millisecond)
	}
}

func main() {
	runApp()
	if polling {
		runPolling()
	} else {
		runInotify()
	}
}
<commit_msg>refactored process restart to its own function<commit_after>package main

import (
	"syscall"
	"bytes"
	"encoding/binary"
	"flag"
	"log"
	"os"
	"os/exec"
	"io/ioutil"
	"strings"
	"time"
)

const (
	EVENT_SIZE = 16
)

type inotifyEvent struct {
	wd int32
	mask int32
	cookie int32
	length int32
	name string
}

type polledFile struct {
	path string
	modTime time.Time
}

var (
	path string
	command string
	ext string
	pid int
	polling bool
	lastEvent *inotifyEvent
	pollList map[string] polledFile
	ignoreDir map[string]bool
)

func init() {
	flag.StringVar(&path, "watch", ".", "path to be watched")
	flag.StringVar(&command, "command", "echo", "path to be watched")
	flag.StringVar(&ext, "ext", "go", "extension to be watched")
	flag.BoolVar(&polling, "polling", false, "use polling")
	flag.BoolVar(&polling, "p", false, "use polling")
	flag.Parse()
	ignoreDir := make(map[string]bool,256)
	ignoreDir[".git"] = true
}

func intFromByte(byteSlice []byte, data interface{} ) {
	err := binary.Read(bytes.NewBuffer(byteSlice), binary.LittleEndian, data)
	if err != nil {
		log.Fatal("binary.read failed: ", err)
	}
}

func runApp() {
	log.Print("Starting Process...")
	commandArray := strings.Split(command, " ")
	paramArray := commandArray[1:]
	cmd := exec.Command(commandArray[0], paramArray...)
	err := cmd.Start()
	if err != nil {
		log.Fatal(err)
	}
	log.Print("Process Started Successfuly: ",cmd.Process.Pid)
	pid = cmd.Process.Pid
}

func restartProc() {
	log.Print("Killing Process:  ",pid)
	if proc, err := os.FindProcess(pid); err != nil {
		log.Print("error: ",err)
		runApp()
	}else{
		err := proc.Kill()
		if err != nil {
			log.Print("error: ", err)
		}
		_, err = proc.Wait()
		if err != nil {
			log.Print("error: ", err)
		}
		runApp()
	}
}

//vim test:

func processBuffer(n int, buffer []byte) {
	event := new(inotifyEvent)
	var  i int32

	for i < int32(n) {
		intFromByte(buffer[i: i + 4], &event.wd)
		intFromByte(buffer[i+ 4: i + 8], &event.mask)
		intFromByte(buffer[i + 8: i + 12], &event.cookie)
		intFromByte(buffer[i + 12: i + 16], &event.length)
		event.name =string(buffer[i + 16: i + 16 + event.length])
		event.name = strings.TrimRight(event.name, "\x00")
		i += EVENT_SIZE + event.length

		if(len(strings.Split(event.name,".")) > 1) {
			eventExt := strings.Split(event.name,".")[1]
			log.Print(ext, " - ", eventExt)
			if(ext == eventExt){
				log.Print("I entered the if")
				if lastEvent != nil {
					log.Print(event.name, " - ", lastEvent.name)
					log.Print(event.mask, " - ", lastEvent.mask)
				}else {
					log.Print(event.name)
					log.Print(event.mask)
				}
				if lastEvent != nil && lastEvent.name == event.name && lastEvent.mask == syscall.IN_DELETE && event.mask == syscall.IN_CLOSE_WRITE{
					log.Print("Skipping as we already processed events for file: ", event.name)
					break
				}
				lastEvent = event
				restartProc()
				break
			}
		}
	}
}

func runInotify() {
	fd, err := syscall.InotifyInit()
	if err != nil {
		log.Fatal("error initializing Inotify: ", err)
		return
	}
	addFilesToInotify(fd, path)

	var buffer []byte = make([]byte, 1024 * EVENT_SIZE)

	for {
		n, err := syscall.Read(fd, buffer)
		if err != nil {
			log.Fatal("Read failed: ", err)
			return
		}
		processBuffer(n, buffer)
	}
}

func addFilesToInotify(fd int, dirPath string) {
	dir, err := os.Stat(dirPath)
	if err != nil {
		log.Fatal("error getting info on dir: ", err)
		return
	}
	if dir.IsDir() && dir.Name() != ".git" {
		log.Print("adding: ", dirPath)
		_, err = syscall.InotifyAddWatch(fd, dirPath, syscall.IN_CLOSE_WRITE | syscall.IN_DELETE)
		if err != nil {
			log.Fatal("error adding watch: ", err)
			return
		}

		fileList, err := ioutil.ReadDir(dirPath)
		if err != nil {
			log.Fatal("error reading dir: ", err)
			return
		}
		for _,file := range fileList {
			newPath := dirPath + "/" + file.Name()
			if file.IsDir() && file.Name() != ".git" {
				addFilesToInotify(fd, newPath)
			}
		}
	}
}

func addFilesToPoll(filePath string) {
		fileList, err := ioutil.ReadDir(filePath)
		if err != nil {
			log.Fatal("ReadDir failed: ", err)
		}
		for _,file := range fileList {
			newPath := filePath + "/" + file.Name()
			if file.IsDir() && file.Name() != ".git" {
				pollList[newPath] = polledFile{path:newPath, modTime:file.ModTime()}
				addFilesToPoll(newPath)
			} else {
				fileName := file.Name()
				if(len(strings.Split(fileName,".")) > 1) {
					fileExt := strings.Split(fileName,".")[1]
					if(fileExt == ext){
						pollList[newPath] = polledFile{path:newPath, modTime:file.ModTime()}
						log.Print(fileName, " - ", file.ModTime())
					}
				}
			}
		}
}

func runPolling() {
	pollList = make(map[string] polledFile)
	addFilesToPoll(path)
	for{
		for file,modTime := range pollList {
			fileInfo, err := os.Stat(file)
			if err != nil {
				log.Fatal("Stat error: ", err)
			}
			pollList[file] = polledFile{path: file, modTime:fileInfo.ModTime()}
			log.Print(file, " - ",  modTime)
		}
		time.Sleep(500*time.Millisecond)
	}
}

func main() {
	runApp()
	if polling {
		runPolling()
	} else {
		runInotify()
	}
}
<|endoftext|>


<commit_before>package geo

import (
	"testing"

	"github.com/nfleet/via/geotypes"
)

// The matrix sample we will be using.
var testPayload = struct {
	matrix        []geotypes.Coord
	country       string
	speed_profile int
}{
	[]geotypes.Coord{{21.5, 62.2}, {25.0, 64}},
	"finland",
	120,
}

func erase_computation(hash string, t *testing.T) {
	ok, err := test_geo.Client.Del(hash)
	if err != nil {
		t.Fatalf("deleting %s failed: %s", hash, err.Error())
	}

	if ok, _ = test_geo.Client.Exists(hash); ok {
		t.Fatalf("%s should be deleted", hash)
	}
}

func TestMatrixHashUniqueness(t *testing.T) {
	baseMatrix, baseCountry, baseSpeed := "foo", "finland", 40
	h1 := CreateMatrixHash(baseMatrix, baseCountry, baseSpeed)
	h2 := CreateMatrixHash(baseMatrix, "germany", baseSpeed)
	h3 := CreateMatrixHash(baseMatrix, "germany", 60)

	testUniq := func(s1, s2 string) {
		if s1 == s2 {
			t.Fatalf("%s equal to %s, wtf?", h1, h2)
		}
	}

	testUniq(h1, h2)
	testUniq(h2, h3)
}

func TestMatrixComputationCreation(t *testing.T) {
	hash, res := test_geo.CreateMatrixComputation(testPayload.matrix, testPayload.country, testPayload.speed_profile)
	defer erase_computation(hash, t)

	if res != false {
		t.Errorf("CreateComputation(%q) => %q, wanted false, must create new", testPayload, res)
	}
}

func TestMatrixProxyCreation(t *testing.T) {
	// res ignored, tested by above method.
	hash, _ := test_geo.CreateMatrixComputation(testPayload.matrix, testPayload.country, testPayload.speed_profile)
	defer erase_computation(hash, t)

	// create again
	hash2, res2 := test_geo.CreateMatrixComputation(testPayload.matrix, testPayload.country, testPayload.speed_profile)
	defer erase_computation(hash2, t)

	if hash == hash2 {
		t.Errorf("Proxy creation failed: %q == %q, wanted different", hash, hash2)
	}

	if res2 != true {
		t.Error("Proxy creation didn't succeed.")
	}

	ttl, _ := test_geo.Client.Ttl(hash)
	ttl2, _ := test_geo.Client.Ttl(hash2)

	if ttl != ttl2 {
		t.Errorf("TTL different: %q != %q, TTL times should be equal for proxy resources.", ttl, ttl2)
	}

}
<commit_msg>Fix package name.<commit_after>package main

import (
	"testing"

	"github.com/nfleet/via/geotypes"
)

// The matrix sample we will be using.
var testPayload = struct {
	matrix        []geotypes.Coord
	country       string
	speed_profile int
}{
	[]geotypes.Coord{{21.5, 62.2}, {25.0, 64}},
	"finland",
	120,
}

func erase_computation(hash string, t *testing.T) {
	ok, err := test_geo.Client.Del(hash)
	if err != nil {
		t.Fatalf("deleting %s failed: %s", hash, err.Error())
	}

	if ok, _ = test_geo.Client.Exists(hash); ok {
		t.Fatalf("%s should be deleted", hash)
	}
}

func TestMatrixHashUniqueness(t *testing.T) {
	baseMatrix, baseCountry, baseSpeed := "foo", "finland", 40
	h1 := CreateMatrixHash(baseMatrix, baseCountry, baseSpeed)
	h2 := CreateMatrixHash(baseMatrix, "germany", baseSpeed)
	h3 := CreateMatrixHash(baseMatrix, "germany", 60)

	testUniq := func(s1, s2 string) {
		if s1 == s2 {
			t.Fatalf("%s equal to %s, wtf?", h1, h2)
		}
	}

	testUniq(h1, h2)
	testUniq(h2, h3)
}

func TestMatrixComputationCreation(t *testing.T) {
	hash, res := test_geo.CreateMatrixComputation(testPayload.matrix, testPayload.country, testPayload.speed_profile)
	defer erase_computation(hash, t)

	if res != false {
		t.Errorf("CreateComputation(%q) => %q, wanted false, must create new", testPayload, res)
	}
}

func TestMatrixProxyCreation(t *testing.T) {
	// res ignored, tested by above method.
	hash, _ := test_geo.CreateMatrixComputation(testPayload.matrix, testPayload.country, testPayload.speed_profile)
	defer erase_computation(hash, t)

	// create again
	hash2, res2 := test_geo.CreateMatrixComputation(testPayload.matrix, testPayload.country, testPayload.speed_profile)
	defer erase_computation(hash2, t)

	if hash == hash2 {
		t.Errorf("Proxy creation failed: %q == %q, wanted different", hash, hash2)
	}

	if res2 != true {
		t.Error("Proxy creation didn't succeed.")
	}

	ttl, _ := test_geo.Client.Ttl(hash)
	ttl2, _ := test_geo.Client.Ttl(hash2)

	if ttl != ttl2 {
		t.Errorf("TTL different: %q != %q, TTL times should be equal for proxy resources.", ttl, ttl2)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math/big"
	"reflect"
	"regexp"
	"strings"
)

var (
	// we match <magnitude> <unit>
	// where <magnitude> can look like:
	//   2
	//   2.5
	//   2 1/2
	// and <unit> can look like
	//   oz
	//   cup
	//   cups
	//   tsp
	//   etc...
	re = regexp.MustCompile(`^\s*(\d+\s+\d+/\d+|\d+|\d+\.\d+|\d+/\d+)\s+([a-zA-Z]*)\s*$`)
)

type System int8

const (
	Metric System = iota
	Imperial
)

type OutputType int8

const (
	NeverOutput = iota
	SingleOnly
	MultiplesOK
	MultiplesOKAppendS
)

type Volume int64

const (
	Milliliter Volume = 8
	Deciliter         = Milliliter * 100
	Liter             = Milliliter * 1000

	EighthTeaspoon  = Milliliter * 5 / 8
	QuarterTeaspoon = EighthTeaspoon * 2
	HalfTeaspoon    = QuarterTeaspoon * 2
	Teaspoon        = HalfTeaspoon * 2
	Tablespoon      = Teaspoon * 3
	FluidOunce      = Tablespoon * 2
	QuarterCup      = FluidOunce * 2
	HalfCup         = FluidOunce * 4
	Cup             = FluidOunce * 8
	Pint            = Cup * 2
	Quart           = Cup * 4
	Gallon          = Quart * 4
)

type unitInfo struct {
	measurement   Measurement
	system        System
	out           string
	in            []string
	decimalPlaces int8
	outputType    OutputType
}

var (
	volumeInfo = map[Measurement]*unitInfo{
		Milliliter: &unitInfo{
			nil,
			Metric,
			"ml",
			[]string{"ml", "milliliter", "milliliters", "millilitre", "millilitres", "mL"},
			0,
			MultiplesOK,
		},
		Deciliter: &unitInfo{
			nil,
			Metric,
			"",
			[]string{"dl", "deciliter", "deciliters", "decilitre", "decilitres", "dL"},
			0,
			NeverOutput,
		},
		Liter: &unitInfo{
			nil,
			Metric,
			"l",
			[]string{"l", "liter", "liters", "litre", "litres", "L"},
			3,
			MultiplesOK,
		},

		EighthTeaspoon: &unitInfo{
			nil,
			Imperial,
			"1/8 tsp",
			[]string{},
			0,
			SingleOnly,
		},
		QuarterTeaspoon: &unitInfo{
			nil,
			Imperial,
			"1/4 tsp",
			[]string{},
			0,
			SingleOnly,
		},
		HalfTeaspoon: &unitInfo{
			nil,
			Imperial,
			"1/2 tsp",
			[]string{},
			0,
			SingleOnly,
		},
		Teaspoon: &unitInfo{
			nil,
			Imperial,
			"t",
			[]string{"t", "teaspoon", "teaspoons", "tsp.", "tsp"},
			0,
			SingleOnly,
		},
		Tablespoon: &unitInfo{
			nil,
			Imperial,
			"T",
			[]string{"T", "tablespoon", "tablespoons", "tbl.", "tbl", "tbs.", "tbsp."},
			0,
			SingleOnly,
		},
		FluidOunce: &unitInfo{
			nil,
			Imperial,
			"",
			[]string{"fluid ounce", "fluid ounces", "fl oz"},
			0,
			NeverOutput,
		},
		QuarterCup: &unitInfo{
			nil,
			Imperial,
			"1/4 cup",
			[]string{},
			0,
			SingleOnly,
		},
		HalfCup: &unitInfo{
			nil,
			Imperial,
			"1/2 cup",
			[]string{},
			0,
			SingleOnly,
		},
		Cup: &unitInfo{
			nil,
			Imperial,
			"c",
			[]string{"c", "cup", "cups"},
			0,
			MultiplesOK,
		},
		Pint: &unitInfo{
			nil,
			Imperial,
			"",
			[]string{"p", "pt", "pint", "pints", "fl pt"},
			0,
			NeverOutput,
		},
		Quart: &unitInfo{
			nil,
			Imperial,
			"qt",
			[]string{"q", "quart", "quarts", "qt", "fl qt"},
			0,
			MultiplesOK,
		},
		Gallon: &unitInfo{
			nil,
			Imperial,
			"gal",
			[]string{"gal", "gallon", "gallons", "g"},
			0,
			MultiplesOK,
		},
	}
)

func (v Volume) Add(o Measurement) (result Measurement, err error) {
	if other, ok := o.(Volume); ok {
		return v + other, nil
	} else {
		return v, fmt.Errorf("Volume incompatible with %v", reflect.ValueOf(o).Type())
	}
}

func (v Volume) Mul(r *big.Rat) (Measurement, error) {
	if i, err := mul(int64(v), r); err != nil {
		return Teaspoon, err
	} else {
		return Volume(i), nil
	}
}

type Weight int64

const (
	Milligram Weight = 8
	Gram             = Milligram * 1000
	Kilogram         = Gram * 1000

	Ounce = 28409 * Milligram
	Pound = Ounce * 16
)

func (v Weight) Add(o Measurement) (result Measurement, err error) {
	if other, ok := o.(Weight); ok {
		return v + other, nil
	} else {
		return v, fmt.Errorf("Weight incompatible with %v", reflect.ValueOf(o).Type())
	}
}

func (v Weight) Mul(r *big.Rat) (Measurement, error) {
	if i, err := mul(int64(v), r); err != nil {
		return Ounce, err
	} else {
		return Weight(i), nil
	}
}

type Measurement interface {
	Add(other Measurement) (result Measurement, err error)
	Mul(r *big.Rat) (Measurement, error)
}

func mul(i int64, r *big.Rat) (int64, error) {
	result := big.NewRat(i, 1)
	result.Mul(result, r)
	if !result.IsInt() {
		return 0, fmt.Errorf("Error multiplying %v by %v.  We ended up with non-integral value %v", i, r, result)
	}
	return result.Num().Int64(), nil
}

// Can parse things like "1/4", ".5", "2", "2 1/2"
func parseMagnitude(s string) (*big.Rat, error) {
	tokens := strings.Split(s, " ")
	accum := new(big.Rat)
	r := new(big.Rat)
	for _, t := range tokens {
		if _, ok := r.SetString(t); !ok {
			return nil, fmt.Errorf("Error parsing %v.  Token %v could not be parsed by big.Rat")
		}
		accum.Add(accum, r)
	}
	return accum, nil
}

func Parse(s string) (m Measurement, err error) {
	if matches := re.FindStringSubmatch(s); matches != nil {
		if len(matches) == 3 {
			magnitude := matches[1]
			unit := matches[2]
			log.Printf("%#v: %#v: %#v", s, magnitude, unit)
			if info, ok := measurementLookup[unit]; ok {
				if mag, err := parseMagnitude(magnitude); err != nil {
					return nil, err
				} else {
					return info.measurement.Mul(mag)
				}
			} else {
				return nil, fmt.Errorf("Could not recognize [%v] as unit in %s", unit, s)
			}
		} else {
			return nil, fmt.Errorf("Unable to parse [%v] as measurment.  Matches was %#v", s, matches)
		}
	} else {
		return nil, fmt.Errorf("Unable to parse [%v] as measurement via regexp", s)
	}

}

// TODO(gina) add weight here when we have it
var allMeasurementInfo = []map[Measurement]*unitInfo{volumeInfo}

var measurementLookup = make(map[string]*unitInfo)

func init() {
	for _, m := range allMeasurementInfo {
		for measurement, info := range m {
			info.measurement = measurement

			for _, in := range info.in {
				measurementLookup[in] = info
			}
		}
	}
}

// 	}
// }

// need maps of accepted strings to the matching values

// Is there some kind of 'unit' interface that these both implement?
// Seems like we want a method that accepts a string and returns an
// insance of that unit
<commit_msg>Add weights<commit_after>package main

import (
	"fmt"
	"log"
	"math/big"
	"reflect"
	"regexp"
	"strings"
)

var (
	// we match <magnitude> <unit>
	// where <magnitude> can look like:
	//   2
	//   2.5
	//   2 1/2
	// and <unit> can look like
	//   oz
	//   cup
	//   cups
	//   tsp
	//   etc...
	re = regexp.MustCompile(`^\s*(\d+\s+\d+/\d+|\d+|\d+\.\d+|\d+/\d+)\s+([a-zA-Z]*)\s*$`)
)

type System int8

const (
	Metric System = iota
	Imperial
)

type OutputType int8

const (
	NeverOutput = iota
	SingleOnly
	MultiplesOK
	MultiplesOKAppendS
)

type Volume int64

const (
	Milliliter Volume = 8
	Deciliter         = Milliliter * 100
	Liter             = Milliliter * 1000

	EighthTeaspoon  = Milliliter * 5 / 8
	QuarterTeaspoon = EighthTeaspoon * 2
	HalfTeaspoon    = QuarterTeaspoon * 2
	Teaspoon        = HalfTeaspoon * 2
	Tablespoon      = Teaspoon * 3
	FluidOunce      = Tablespoon * 2
	QuarterCup      = FluidOunce * 2
	HalfCup         = FluidOunce * 4
	Cup             = FluidOunce * 8
	Pint            = Cup * 2
	Quart           = Cup * 4
	Gallon          = Quart * 4
)

type unitInfo struct {
	measurement   Measurement
	system        System
	out           string
	in            []string
	decimalPlaces int8
	outputType    OutputType
}

var (
	volumeInfo = map[Measurement]*unitInfo{
		Milliliter: &unitInfo{
			nil,
			Metric,
			"ml",
			[]string{"ml", "milliliter", "milliliters", "millilitre", "millilitres", "mL"},
			0,
			MultiplesOK,
		},
		Deciliter: &unitInfo{
			nil,
			Metric,
			"",
			[]string{"dl", "deciliter", "deciliters", "decilitre", "decilitres", "dL"},
			0,
			NeverOutput,
		},
		Liter: &unitInfo{
			nil,
			Metric,
			"l",
			[]string{"l", "liter", "liters", "litre", "litres", "L"},
			3,
			MultiplesOK,
		},

		EighthTeaspoon: &unitInfo{
			nil,
			Imperial,
			"1/8 tsp",
			[]string{},
			0,
			SingleOnly,
		},
		QuarterTeaspoon: &unitInfo{
			nil,
			Imperial,
			"1/4 tsp",
			[]string{},
			0,
			SingleOnly,
		},
		HalfTeaspoon: &unitInfo{
			nil,
			Imperial,
			"1/2 tsp",
			[]string{},
			0,
			SingleOnly,
		},
		Teaspoon: &unitInfo{
			nil,
			Imperial,
			"t",
			[]string{"t", "teaspoon", "teaspoons", "tsp.", "tsp"},
			0,
			SingleOnly,
		},
		Tablespoon: &unitInfo{
			nil,
			Imperial,
			"T",
			[]string{"T", "tablespoon", "tablespoons", "tbl.", "tbl", "tbs.", "tbsp."},
			0,
			SingleOnly,
		},
		FluidOunce: &unitInfo{
			nil,
			Imperial,
			"",
			[]string{"fluid ounce", "fluid ounces", "fl oz"},
			0,
			NeverOutput,
		},
		QuarterCup: &unitInfo{
			nil,
			Imperial,
			"1/4 cup",
			[]string{},
			0,
			SingleOnly,
		},
		HalfCup: &unitInfo{
			nil,
			Imperial,
			"1/2 cup",
			[]string{},
			0,
			SingleOnly,
		},
		Cup: &unitInfo{
			nil,
			Imperial,
			"c",
			[]string{"c", "cup", "cups"},
			0,
			MultiplesOK,
		},
		Pint: &unitInfo{
			nil,
			Imperial,
			"",
			[]string{"p", "pt", "pint", "pints", "fl pt"},
			0,
			NeverOutput,
		},
		Quart: &unitInfo{
			nil,
			Imperial,
			"qt",
			[]string{"q", "quart", "quarts", "qt", "fl qt"},
			0,
			MultiplesOK,
		},
		Gallon: &unitInfo{
			nil,
			Imperial,
			"gal",
			[]string{"gal", "gallon", "gallons", "g"},
			0,
			MultiplesOK,
		},
	}
)

func (v Volume) Add(o Measurement) (result Measurement, err error) {
	if other, ok := o.(Volume); ok {
		return v + other, nil
	} else {
		return v, fmt.Errorf("Volume incompatible with %v", reflect.ValueOf(o).Type())
	}
}

func (v Volume) Mul(r *big.Rat) (Measurement, error) {
	if i, err := mul(int64(v), r); err != nil {
		return Teaspoon, err
	} else {
		return Volume(i), nil
	}
}

type Weight int64

const (
	Milligram Weight = 8
	Gram             = Milligram * 1000
	Kilogram         = Gram * 1000

	Ounce = 28409 * Milligram
	Pound = Ounce * 16
)

var (
	weightInfo = map[Measurement]*unitInfo{
		Milligram: &unitInfo{
			nil,
			Metric,
			"mg",
			[]string{"mg", "milligram", "milligrams", "milligramme", "milligrammes"},
			0,
			MultiplesOK,
		},
		Gram: &unitInfo{
			nil,
			Metric,
			"g",
			[]string{"g", "gram", "grams", "gramme", "grammes"},
			3,
			MultiplesOK,
		},
		Kilogram: &unitInfo{
			nil,
			Metric,
			"kg",
			[]string{"kg", "kilogram", "kilograms", "kilogramme", "kilogrammes"},
			3,
			MultiplesOK,
		},

		Ounce: &unitInfo{
			nil,
			Imperial,
			"oz",
			[]string{"oz", "ounce", "ounces"},
			1,
			MultiplesOK,
		},
		Pound: &unitInfo{
			nil,
			Imperial,
			"lb",
			[]string{"lb", "#", "pound", "pounds"},
			0,
			MultiplesOK,
		},
	}
)

func (v Weight) Add(o Measurement) (result Measurement, err error) {
	if other, ok := o.(Weight); ok {
		return v + other, nil
	} else {
		return v, fmt.Errorf("Weight incompatible with %v", reflect.ValueOf(o).Type())
	}
}

func (v Weight) Mul(r *big.Rat) (Measurement, error) {
	if i, err := mul(int64(v), r); err != nil {
		return Ounce, err
	} else {
		return Weight(i), nil
	}
}

type Measurement interface {
	Add(other Measurement) (result Measurement, err error)
	Mul(r *big.Rat) (Measurement, error)
}

func mul(i int64, r *big.Rat) (int64, error) {
	result := big.NewRat(i, 1)
	result.Mul(result, r)
	if !result.IsInt() {
		return 0, fmt.Errorf("Error multiplying %v by %v.  We ended up with non-integral value %v", i, r, result)
	}
	return result.Num().Int64(), nil
}

// Can parse things like "1/4", ".5", "2", "2 1/2"
func parseMagnitude(s string) (*big.Rat, error) {
	tokens := strings.Split(s, " ")
	accum := new(big.Rat)
	r := new(big.Rat)
	for _, t := range tokens {
		if _, ok := r.SetString(t); !ok {
			return nil, fmt.Errorf("Error parsing %v.  Token %v could not be parsed by big.Rat")
		}
		accum.Add(accum, r)
	}
	return accum, nil
}

func Parse(s string) (m Measurement, err error) {
	if matches := re.FindStringSubmatch(s); matches != nil {
		if len(matches) == 3 {
			magnitude := matches[1]
			unit := matches[2]
			log.Printf("%#v: %#v: %#v", s, magnitude, unit)
			if info, ok := measurementLookup[unit]; ok {
				if mag, err := parseMagnitude(magnitude); err != nil {
					return nil, err
				} else {
					return info.measurement.Mul(mag)
				}
			} else {
				return nil, fmt.Errorf("Could not recognize [%v] as unit in %s", unit, s)
			}
		} else {
			return nil, fmt.Errorf("Unable to parse [%v] as measurment.  Matches was %#v", s, matches)
		}
	} else {
		return nil, fmt.Errorf("Unable to parse [%v] as measurement via regexp", s)
	}

}

var allMeasurementInfo = []map[Measurement]*unitInfo{volumeInfo, weightInfo}

var measurementLookup = make(map[string]*unitInfo)

func init() {
	for _, m := range allMeasurementInfo {
		for measurement, info := range m {
			info.measurement = measurement

			for _, in := range info.in {
				measurementLookup[in] = info
			}
		}
	}
}

// 	}
// }

// need maps of accepted strings to the matching values

// Is there some kind of 'unit' interface that these both implement?
// Seems like we want a method that accepts a string and returns an
// insance of that unit
<|endoftext|>


<commit_before>package mesos

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
)

type Mesos struct {
	Location    string
	Protocol    string
	Principal   string
	Secret      string
	NoVerifySsl bool
}

func DefaultConfig() *Mesos {
	return &Mesos{
		Location:    "localhost:5050",
		Protocol:    "http",
		NoVerifySsl: false,
		Principal:   "mantl-install",
	}
}

type Framework struct {
	Name   string `json:"name"`
	ID     string `json:"id"`
	Active bool   `json:"active"`
}

type State struct {
	CompletedFrameworks    []*Framework `json:"completed_frameworks"`
	Frameworks             []*Framework `json:"frameworks"`
	UnregisteredFrameworks []*Framework `json:"unregistered_frameworks"`
}

func NewMesos(location, protocol string, principal string, secret string, verifySsl bool) *Mesos {
	return &Mesos{location, protocol, principal, secret, verifySsl}
}

func (m Mesos) Frameworks() ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	return state.Frameworks, nil
}

func (m Mesos) Shutdown(frameworkId string) error {
	log.Debugf("Shutting down framework: %s", frameworkId)
	data := fmt.Sprintf("frameworkId=%s", frameworkId)
	response, err := m.post("/master/teardown", []byte(data))
	if err != nil {
		return err
	}
	if response.StatusCode == 200 {
		return nil
	} else {
		responseText, _ := m.responseText(response)
		return errors.New(fmt.Sprintf("Could not shutdown framework %s: %d %s", frameworkId, response.StatusCode, responseText))
	}
}

func (m Mesos) ShutdownFrameworkByName(name string) error {
	log.Debugf("Looking for %s framework", name)

	// find mesos framework
	fw, err := m.FindFramework(name)
	if err != nil {
		return err
	}

	if fw == nil {
		log.Debugf("Framework %s not active", name)
		return nil
	}

	// shutdown mesos framework
	return m.Shutdown(fw.ID)
}

func (m Mesos) FindFrameworks(name string) ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	matching := make(map[string]*Framework)
	for _, fw := range state.Frameworks {
		if fw.Name == name && fw.Active {
			matching[fw.ID] = fw
		}
	}

	var uniqueFws []*Framework
	for _, fw := range matching {
		uniqueFws = append(uniqueFws, fw)
	}

	return uniqueFws, nil
}

func (m Mesos) FindFramework(name string) (*Framework, error) {
	fws, err := m.FindFrameworks(name)
	if err != nil {
		return nil, err
	}

	fwCount := len(fws)
	if fwCount == 0 {
		return nil, nil
	} else if fwCount > 1 {
		return nil, errors.New(fmt.Sprintf("There are %d %s frameworks.", fwCount, name))
	}

	return fws[0], nil
}

func (m Mesos) state() (*State, error) {
	response, err := m.get("/master/state.json")
	if err != nil {
		return nil, err
	}

	body, err := m.responseBody(response)
	if err != nil {
		return nil, err
	}

	state := &State{}
	err = json.Unmarshal(body, state)
	return state, err
}

func (m Mesos) get(url string) (*http.Response, error) {
	return m.doRequest("GET", url, nil)
}

func (m Mesos) delete(url string) (*http.Response, error) {
	return m.doRequest("DELETE", url, nil)
}

func (m Mesos) post(url string, data []byte) (*http.Response, error) {
	return m.doRequest("POST", url, data)
}

func (m Mesos) doRequest(method string, path string, data []byte) (*http.Response, error) {
	url := m.url(path)
	client := m.getClient()

	var buf io.Reader
	if len(data) > 0 {
		buf = bytes.NewBuffer(data)
	}

	log.Debugf("%s %s", method, url)
	request, err := http.NewRequest(method, url, buf)
	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept", "application/json")

	if m.Principal != "" && m.Secret != "" {
		request.SetBasicAuth(m.Principal, m.Secret)
	}

	if err != nil {
		log.WithFields(log.Fields{
			"method": method,
			"url":    url,
		}).Error(err)
		return nil, err
	}

	response, err := client.Do(request)
	m.logHTTP(response, method, url, err)

	return response, err
}

func (m Mesos) getClient() *http.Client {
	client := &http.Client{}
	client.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: m.NoVerifySsl,
		},
	}

	return client
}

func (m Mesos) url(path string) string {
	u := url.URL{
		Scheme: m.Protocol,
		Host:   m.Location,
		Path:   path,
	}

	return u.String()
}

func (m Mesos) responseBody(response *http.Response) ([]byte, error) {
	if response == nil {
		return nil, nil
	}

	defer response.Body.Close()
	body, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Errorf("Could not read response body: %v", err)
		return nil, err
	}

	return body, nil
}

func (m Mesos) responseText(response *http.Response) (string, error) {
	responseText := ""

	body, err := m.responseBody(response)
	if err != nil {
		return responseText, err
	}

	if len(body) > 0 {
		responseText = string(body)
	}

	return responseText, nil
}

func (m Mesos) logHTTP(resp *http.Response, method string, url string, err error) {
	fields := log.Fields{
		"url":    url,
		"method": method,
	}

	if resp != nil {
		fields["status"] = resp.Status
		fields["statusCode"] = resp.StatusCode
	}

	if err != nil {
		log.WithFields(fields).Error(err.Error())
	} else {
		log.WithFields(fields).Debugf("%s %s", method, url)
	}
}
<commit_msg>refactor - unified http interface<commit_after>package mesos

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/CiscoCloud/mantl-api/utils/http"
	log "github.com/Sirupsen/logrus"
)

type Mesos struct {
	Principal  string
	Secret     string
	httpClient *http.HttpClient
}

type Framework struct {
	Name   string `json:"name"`
	ID     string `json:"id"`
	Active bool   `json:"active"`
}

type State struct {
	CompletedFrameworks    []*Framework `json:"completed_frameworks"`
	Frameworks             []*Framework `json:"frameworks"`
	UnregisteredFrameworks []*Framework `json:"unregistered_frameworks"`
}

func NewMesos(location, protocol string, principal string, secret string, noVerifySsl bool) *Mesos {
	return &Mesos{
		Principal: principal,
		Secret:    secret,
		httpClient: &http.HttpClient{
			Location:    location,
			Protocol:    protocol,
			Username:    principal,
			Password:    secret,
			NoVerifySsl: noVerifySsl,
		},
	}
}

func (m Mesos) Frameworks() ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	return state.Frameworks, nil
}

func (m Mesos) Shutdown(frameworkId string) error {
	log.Debugf("Shutting down framework: %s", frameworkId)
	data := fmt.Sprintf("frameworkId=%s", frameworkId)
	httpReq, err := m.httpClient.Post("/master/teardown", []byte(data))
	if err != nil {
		return err
	}
	if httpReq.Response.StatusCode == 200 {
		return nil
	} else {
		responseText := httpReq.ResponseText
		return errors.New(fmt.Sprintf("Could not shutdown framework %s: %d %s", frameworkId, httpReq.Response.StatusCode, responseText))
	}
}

func (m Mesos) ShutdownFrameworkByName(name string) error {
	log.Debugf("Looking for %s framework", name)

	// find mesos framework
	fw, err := m.FindFramework(name)
	if err != nil {
		return err
	}

	if fw == nil {
		log.Debugf("Framework %s not active", name)
		return nil
	}

	// shutdown mesos framework
	return m.Shutdown(fw.ID)
}

func (m Mesos) FindFrameworks(name string) ([]*Framework, error) {
	state, err := m.state()
	if err != nil {
		return []*Framework{}, err
	}

	matching := make(map[string]*Framework)
	for _, fw := range state.Frameworks {
		if fw.Name == name && fw.Active {
			matching[fw.ID] = fw
		}
	}

	var uniqueFws []*Framework
	for _, fw := range matching {
		uniqueFws = append(uniqueFws, fw)
	}

	return uniqueFws, nil
}

func (m Mesos) FindFramework(name string) (*Framework, error) {
	fws, err := m.FindFrameworks(name)
	if err != nil {
		return nil, err
	}

	fwCount := len(fws)
	if fwCount == 0 {
		return nil, nil
	} else if fwCount > 1 {
		return nil, errors.New(fmt.Sprintf("There are %d %s frameworks.", fwCount, name))
	}

	return fws[0], nil
}

func (m Mesos) state() (*State, error) {
	httpReq, err := m.httpClient.Get("/master/state.json")
	if err != nil {
		return nil, err
	}

	body := httpReq.ResponseBody
	if err != nil {
		return nil, err
	}

	state := &State{}
	err = json.Unmarshal(body, state)
	return state, err
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 gcscaching_test

import (
	"errors"
	"testing"
	"time"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	"github.com/jacobsa/gcloud/gcs/gcscaching/mock_gcscaching"
	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
)

func TestFastStatBucket(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

const ttl = time.Second

type fastStatBucketTest struct {
	cache   mock_gcscaching.MockStatCache
	clock   timeutil.SimulatedClock
	wrapped mock_gcs.MockBucket

	bucket gcs.Bucket
}

func (t *fastStatBucketTest) SetUp(ti *TestInfo) {
	// Set up a fixed, non-zero time.
	t.clock.SetTime(time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local))

	// Set up dependencies.
	t.cache = mock_gcscaching.NewMockStatCache(ti.MockController, "cache")
	t.wrapped = mock_gcs.NewMockBucket(ti.MockController, "wrapped")

	t.bucket = gcscaching.NewFastStatBucket(
		ttl,
		t.cache,
		&t.clock,
		t.wrapped)
}

////////////////////////////////////////////////////////////////////////
// CreateObject
////////////////////////////////////////////////////////////////////////

type CreateObjectTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&CreateObjectTest{}) }

func (t *CreateObjectTest) CallsEraseAndWrapped() {
	const name = "taco"

	// Erase
	ExpectCall(t.cache, "Erase")(name)

	// Wrapped
	var wrappedReq *gcs.CreateObjectRequest
	ExpectCall(t.wrapped, "CreateObject")(Any(), Any()).
		WillOnce(DoAll(SaveArg(1, &wrappedReq), Return(nil, errors.New(""))))

	// Call
	req := &gcs.CreateObjectRequest{
		Name: name,
	}

	_, _ = t.bucket.CreateObject(nil, req)

	AssertNe(nil, wrappedReq)
	ExpectEq(req, wrappedReq)
}

func (t *CreateObjectTest) WrappedFails() {
	const name = ""
	var err error

	// Erase
	ExpectCall(t.cache, "Erase")(Any())

	// Wrapped
	ExpectCall(t.wrapped, "CreateObject")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err = t.bucket.CreateObject(nil, &gcs.CreateObjectRequest{})

	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *CreateObjectTest) WrappedSucceeds() {
	const name = "taco"
	var err error

	// Erase
	ExpectCall(t.cache, "Erase")(Any())

	// Wrapped
	obj := &gcs.Object{
		Name:       name,
		Generation: 1234,
	}

	ExpectCall(t.wrapped, "CreateObject")(Any(), Any()).
		WillOnce(Return(obj, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(obj, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	o, err := t.bucket.CreateObject(nil, &gcs.CreateObjectRequest{})

	AssertEq(nil, err)
	ExpectEq(obj, o)
}

////////////////////////////////////////////////////////////////////////
// StatObject
////////////////////////////////////////////////////////////////////////

type StatObjectTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&StatObjectTest{}) }

func (t *StatObjectTest) CallsCache() {
	const name = "taco"

	// LookUp
	ExpectCall(t.cache, "LookUp")(name, timeutil.TimeEq(t.clock.Now())).
		WillOnce(Return(&gcs.Object{}))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	_, _ = t.bucket.StatObject(nil, req)
}

func (t *StatObjectTest) CacheHit() {
	const name = "taco"

	// LookUp
	obj := &gcs.Object{
		Name: name,
	}

	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(obj))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	o, err := t.bucket.StatObject(nil, req)
	AssertEq(nil, err)
	ExpectEq(obj, o)
}

func (t *StatObjectTest) CallsWrapped() {
	const name = ""
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	// LookUp
	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(nil))

	// Wrapped
	ExpectCall(t.wrapped, "StatObject")(Any(), req).
		WillOnce(Return(nil, errors.New("")))

	// Call
	_, _ = t.bucket.StatObject(nil, req)
}

func (t *StatObjectTest) WrappedFails() {
	const name = ""

	// LookUp
	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(nil))

	// Wrapped
	ExpectCall(t.wrapped, "StatObject")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	_, err := t.bucket.StatObject(nil, req)
	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *StatObjectTest) WrappedSucceeds() {
	const name = "taco"

	// LookUp
	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(nil))

	// Wrapped
	obj := &gcs.Object{
		Name: name,
	}

	ExpectCall(t.wrapped, "StatObject")(Any(), Any()).
		WillOnce(Return(obj, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(obj, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	o, err := t.bucket.StatObject(nil, req)
	AssertEq(nil, err)
	ExpectEq(obj, o)
}

////////////////////////////////////////////////////////////////////////
// ListObjects
////////////////////////////////////////////////////////////////////////

type ListObjectsTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&ListObjectsTest{}) }

func (t *ListObjectsTest) WrappedFails() {
	// Wrapped
	ExpectCall(t.wrapped, "ListObjects")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err := t.bucket.ListObjects(nil, &gcs.ListObjectsRequest{})
	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *ListObjectsTest) EmptyListing() {
	// Wrapped
	expected := &gcs.Listing{}

	ExpectCall(t.wrapped, "ListObjects")(Any(), Any()).
		WillOnce(Return(expected, nil))

	// Call
	listing, err := t.bucket.ListObjects(nil, &gcs.ListObjectsRequest{})

	AssertEq(nil, err)
	ExpectEq(expected, listing)
}

func (t *ListObjectsTest) NonEmptyListing() {
	// Wrapped
	o0 := &gcs.Object{Name: "taco"}
	o1 := &gcs.Object{Name: "burrito"}

	expected := &gcs.Listing{
		Objects: []*gcs.Object{o0, o1},
	}

	ExpectCall(t.wrapped, "ListObjects")(Any(), Any()).
		WillOnce(Return(expected, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(o0, timeutil.TimeEq(t.clock.Now().Add(ttl)))
	ExpectCall(t.cache, "Insert")(o1, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	listing, err := t.bucket.ListObjects(nil, &gcs.ListObjectsRequest{})

	AssertEq(nil, err)
	ExpectEq(expected, listing)
}
<commit_msg>UpdateObjectTest<commit_after>// Copyright 2015 Google Inc. 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 gcscaching_test

import (
	"errors"
	"testing"
	"time"

	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcscaching"
	"github.com/jacobsa/gcloud/gcs/gcscaching/mock_gcscaching"
	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
)

func TestFastStatBucket(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

const ttl = time.Second

type fastStatBucketTest struct {
	cache   mock_gcscaching.MockStatCache
	clock   timeutil.SimulatedClock
	wrapped mock_gcs.MockBucket

	bucket gcs.Bucket
}

func (t *fastStatBucketTest) SetUp(ti *TestInfo) {
	// Set up a fixed, non-zero time.
	t.clock.SetTime(time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local))

	// Set up dependencies.
	t.cache = mock_gcscaching.NewMockStatCache(ti.MockController, "cache")
	t.wrapped = mock_gcs.NewMockBucket(ti.MockController, "wrapped")

	t.bucket = gcscaching.NewFastStatBucket(
		ttl,
		t.cache,
		&t.clock,
		t.wrapped)
}

////////////////////////////////////////////////////////////////////////
// CreateObject
////////////////////////////////////////////////////////////////////////

type CreateObjectTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&CreateObjectTest{}) }

func (t *CreateObjectTest) CallsEraseAndWrapped() {
	const name = "taco"

	// Erase
	ExpectCall(t.cache, "Erase")(name)

	// Wrapped
	var wrappedReq *gcs.CreateObjectRequest
	ExpectCall(t.wrapped, "CreateObject")(Any(), Any()).
		WillOnce(DoAll(SaveArg(1, &wrappedReq), Return(nil, errors.New(""))))

	// Call
	req := &gcs.CreateObjectRequest{
		Name: name,
	}

	_, _ = t.bucket.CreateObject(nil, req)

	AssertNe(nil, wrappedReq)
	ExpectEq(req, wrappedReq)
}

func (t *CreateObjectTest) WrappedFails() {
	const name = ""
	var err error

	// Erase
	ExpectCall(t.cache, "Erase")(Any())

	// Wrapped
	ExpectCall(t.wrapped, "CreateObject")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err = t.bucket.CreateObject(nil, &gcs.CreateObjectRequest{})

	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *CreateObjectTest) WrappedSucceeds() {
	const name = "taco"
	var err error

	// Erase
	ExpectCall(t.cache, "Erase")(Any())

	// Wrapped
	obj := &gcs.Object{
		Name:       name,
		Generation: 1234,
	}

	ExpectCall(t.wrapped, "CreateObject")(Any(), Any()).
		WillOnce(Return(obj, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(obj, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	o, err := t.bucket.CreateObject(nil, &gcs.CreateObjectRequest{})

	AssertEq(nil, err)
	ExpectEq(obj, o)
}

////////////////////////////////////////////////////////////////////////
// StatObject
////////////////////////////////////////////////////////////////////////

type StatObjectTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&StatObjectTest{}) }

func (t *StatObjectTest) CallsCache() {
	const name = "taco"

	// LookUp
	ExpectCall(t.cache, "LookUp")(name, timeutil.TimeEq(t.clock.Now())).
		WillOnce(Return(&gcs.Object{}))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	_, _ = t.bucket.StatObject(nil, req)
}

func (t *StatObjectTest) CacheHit() {
	const name = "taco"

	// LookUp
	obj := &gcs.Object{
		Name: name,
	}

	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(obj))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	o, err := t.bucket.StatObject(nil, req)
	AssertEq(nil, err)
	ExpectEq(obj, o)
}

func (t *StatObjectTest) CallsWrapped() {
	const name = ""
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	// LookUp
	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(nil))

	// Wrapped
	ExpectCall(t.wrapped, "StatObject")(Any(), req).
		WillOnce(Return(nil, errors.New("")))

	// Call
	_, _ = t.bucket.StatObject(nil, req)
}

func (t *StatObjectTest) WrappedFails() {
	const name = ""

	// LookUp
	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(nil))

	// Wrapped
	ExpectCall(t.wrapped, "StatObject")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	_, err := t.bucket.StatObject(nil, req)
	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *StatObjectTest) WrappedSucceeds() {
	const name = "taco"

	// LookUp
	ExpectCall(t.cache, "LookUp")(Any(), Any()).
		WillOnce(Return(nil))

	// Wrapped
	obj := &gcs.Object{
		Name: name,
	}

	ExpectCall(t.wrapped, "StatObject")(Any(), Any()).
		WillOnce(Return(obj, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(obj, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	req := &gcs.StatObjectRequest{
		Name: name,
	}

	o, err := t.bucket.StatObject(nil, req)
	AssertEq(nil, err)
	ExpectEq(obj, o)
}

////////////////////////////////////////////////////////////////////////
// ListObjects
////////////////////////////////////////////////////////////////////////

type ListObjectsTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&ListObjectsTest{}) }

func (t *ListObjectsTest) WrappedFails() {
	// Wrapped
	ExpectCall(t.wrapped, "ListObjects")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err := t.bucket.ListObjects(nil, &gcs.ListObjectsRequest{})
	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *ListObjectsTest) EmptyListing() {
	// Wrapped
	expected := &gcs.Listing{}

	ExpectCall(t.wrapped, "ListObjects")(Any(), Any()).
		WillOnce(Return(expected, nil))

	// Call
	listing, err := t.bucket.ListObjects(nil, &gcs.ListObjectsRequest{})

	AssertEq(nil, err)
	ExpectEq(expected, listing)
}

func (t *ListObjectsTest) NonEmptyListing() {
	// Wrapped
	o0 := &gcs.Object{Name: "taco"}
	o1 := &gcs.Object{Name: "burrito"}

	expected := &gcs.Listing{
		Objects: []*gcs.Object{o0, o1},
	}

	ExpectCall(t.wrapped, "ListObjects")(Any(), Any()).
		WillOnce(Return(expected, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(o0, timeutil.TimeEq(t.clock.Now().Add(ttl)))
	ExpectCall(t.cache, "Insert")(o1, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	listing, err := t.bucket.ListObjects(nil, &gcs.ListObjectsRequest{})

	AssertEq(nil, err)
	ExpectEq(expected, listing)
}

////////////////////////////////////////////////////////////////////////
// UpdateObject
////////////////////////////////////////////////////////////////////////

type UpdateObjectTest struct {
	fastStatBucketTest
}

func init() { RegisterTestSuite(&UpdateObjectTest{}) }

func (t *UpdateObjectTest) CallsEraseAndWrapped() {
	const name = "taco"

	// Erase
	ExpectCall(t.cache, "Erase")(name)

	// Wrapped
	var wrappedReq *gcs.UpdateObjectRequest
	ExpectCall(t.wrapped, "UpdateObject")(Any(), Any()).
		WillOnce(DoAll(SaveArg(1, &wrappedReq), Return(nil, errors.New(""))))

	// Call
	req := &gcs.UpdateObjectRequest{
		Name: name,
	}

	_, _ = t.bucket.UpdateObject(nil, req)

	AssertNe(nil, wrappedReq)
	ExpectEq(req, wrappedReq)
}

func (t *UpdateObjectTest) WrappedFails() {
	const name = ""
	var err error

	// Erase
	ExpectCall(t.cache, "Erase")(Any())

	// Wrapped
	ExpectCall(t.wrapped, "UpdateObject")(Any(), Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	_, err = t.bucket.UpdateObject(nil, &gcs.UpdateObjectRequest{})

	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *UpdateObjectTest) WrappedSucceeds() {
	const name = "taco"
	var err error

	// Erase
	ExpectCall(t.cache, "Erase")(Any())

	// Wrapped
	obj := &gcs.Object{
		Name:       name,
		Generation: 1234,
	}

	ExpectCall(t.wrapped, "UpdateObject")(Any(), Any()).
		WillOnce(Return(obj, nil))

	// Insert
	ExpectCall(t.cache, "Insert")(obj, timeutil.TimeEq(t.clock.Now().Add(ttl)))

	// Call
	o, err := t.bucket.UpdateObject(nil, &gcs.UpdateObjectRequest{})

	AssertEq(nil, err)
	ExpectEq(obj, o)
}
<|endoftext|>


<commit_before>package evepraisal

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/evepraisal/go-evepraisal/parsers"
)

type Appraisal struct {
	ID         string `json:"id"`
	Created    int64  `json:"created"`
	Kind       string `json:"kind"`
	MarketID   int    `json:"market_id"`
	MarketName string `json:"market_name"`
	Totals     struct {
		Buy    float64 `json:"buy"`
		Sell   float64 `json:"sell"`
		Volume int64   `json:"volume"`
	} `json:"totals"`
	Items    []AppraisalItem `json:"items"`
	Raw      string          `json:"raw"`
	Unparsed map[int]string  `json:"unparsed"`
}

func StringToAppraisal(s string) (*Appraisal, error) {
	appraisal := &Appraisal{
		Created: time.Now().Unix(),
		Raw:     s,
	}

	result, unparsed := parsers.AllParser(parsers.StringToInput(s))
	appraisal.Unparsed = map[int]string(unparsed)

	kind, err := findKind(result)
	if err != nil {
		return appraisal, err
	}
	appraisal.Kind = kind

	items := parserResultToAppraisalItems(result)
	for i := 0; i < len(items); i++ {
		t, ok := TypeMap[strings.ToLower(items[i].Name)]
		if !ok {
			log.Println("WARN: parsed out name that isn't a type", items[i].Name)
			continue
		}
		items[i].TypeID = t.Type.ID
		items[i].TypeName = t.Type.Name

		prices, ok := PriceMap[t.Type.ID]
		if !ok {
			log.Println("WARN: No market data for type (%d %s)", items[i].TypeID, items[i].TypeName)
			continue
		}
		items[i].Prices = prices

		appraisal.Totals.Buy += prices.Buy.Max * float64(items[i].Quantity)
		appraisal.Totals.Sell += prices.Sell.Min * float64(items[i].Quantity)
		appraisal.Totals.Volume += prices.All.Volume * items[i].Quantity
	}
	appraisal.Items = items

	return appraisal, nil
}

type AppraisalItem struct {
	Name     string                 `json:"name"`
	TypeID   int64                  `json:"typeID"`
	TypeName string                 `json:"typeName"`
	Quantity int64                  `json:"quantity"`
	Meta     map[string]interface{} `json:"meta"`
	Prices   Prices                 `json:"prices"`
}

func (i AppraisalItem) SellTotal() float64 {
	return float64(i.Quantity) * i.Prices.Sell.Min
}

func (i AppraisalItem) BuyTotal() float64 {
	return float64(i.Quantity) * i.Prices.Sell.Max
}

type Prices struct {
	All  PriceStats `json:"all"`
	Buy  PriceStats `json:"buy"`
	Sell PriceStats `json:"sell"`
}

type PriceStats struct {
	Average    float64 `json:"avg"`
	Max        float64 `json:"max"`
	Median     float64 `json:"median"`
	Min        float64 `json:"min"`
	Percentile float64 `json:"percentile"`
	Stddev     float64 `json:"stddev"`
	Volume     int64   `json:"volume"`
}

func findKind(result parsers.ParserResult) (string, error) {
	largestLines := -1
	largestLinesParser := "unknown"
	switch r := result.(type) {
	default:
		return largestLinesParser, fmt.Errorf("unexpected type %T", r)
	case *parsers.MultiParserResult:
		if len(r.Results) == 0 {
			return largestLinesParser, fmt.Errorf("No valid lines found")
		}
		for _, subResult := range r.Results {
			if len(subResult.Lines()) > largestLines {
				largestLines = len(subResult.Lines())
				largestLinesParser = subResult.Name()
			}
		}
	}
	return largestLinesParser, nil
}

func parserResultToAppraisalItems(result parsers.ParserResult) []AppraisalItem {
	var items []AppraisalItem
	switch r := result.(type) {
	default:
		log.Printf("unexpected type %T", r)
	case *parsers.MultiParserResult:
		for _, subResult := range r.Results {
			items = append(items, parserResultToAppraisalItems(subResult)...)
		}
	case *parsers.AssetList:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.CargoScan:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Contract:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta:     map[string]interface{}{"fitted": item.Fitted},
				})
		}
	case *parsers.DScan:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: 1})
		}
	case *parsers.EFT:
		items = append(items, AppraisalItem{Name: r.Ship, Quantity: 1})
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Fitting:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Industry:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Killmail:
		for _, item := range r.Dropped {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"dropped":  true,
						"location": item.Location,
					},
				})
		}
		for _, item := range r.Destroyed {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"destroyed": true,
						"location":  item.Location,
					},
				})
		}
	case *parsers.Listing:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.LootHistory:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"player_name": item.PlayerName,
					},
				})
		}
	case *parsers.PI:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"routed": item.Routed,
						"volume": item.Volume,
					},
				})
		}
	case *parsers.SurveyScan:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"distance": item.Distance,
					},
				})
		}
	case *parsers.ViewContents:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"location": item.Location,
					},
				})
		}
	case *parsers.Wallet:
		for _, item := range r.ItemizedTransactions {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"client":   item.Client,
						"credit":   item.Credit,
						"currency": item.Currency,
						"datetime": item.Datetime,
						"location": item.Location,
						"price":    item.Price,
					},
				})
		}
	}
	return items
}
<commit_msg>Fix go vet issue<commit_after>package evepraisal

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/evepraisal/go-evepraisal/parsers"
)

type Appraisal struct {
	ID         string `json:"id"`
	Created    int64  `json:"created"`
	Kind       string `json:"kind"`
	MarketID   int    `json:"market_id"`
	MarketName string `json:"market_name"`
	Totals     struct {
		Buy    float64 `json:"buy"`
		Sell   float64 `json:"sell"`
		Volume int64   `json:"volume"`
	} `json:"totals"`
	Items    []AppraisalItem `json:"items"`
	Raw      string          `json:"raw"`
	Unparsed map[int]string  `json:"unparsed"`
}

func StringToAppraisal(s string) (*Appraisal, error) {
	appraisal := &Appraisal{
		Created: time.Now().Unix(),
		Raw:     s,
	}

	result, unparsed := parsers.AllParser(parsers.StringToInput(s))
	appraisal.Unparsed = map[int]string(unparsed)

	kind, err := findKind(result)
	if err != nil {
		return appraisal, err
	}
	appraisal.Kind = kind

	items := parserResultToAppraisalItems(result)
	for i := 0; i < len(items); i++ {
		t, ok := TypeMap[strings.ToLower(items[i].Name)]
		if !ok {
			log.Printf("WARN: parsed out name that isn't a type: %s", items[i].Name)
			continue
		}
		items[i].TypeID = t.Type.ID
		items[i].TypeName = t.Type.Name

		prices, ok := PriceMap[t.Type.ID]
		if !ok {
			log.Printf("WARN: No market data for type (%d %s)", items[i].TypeID, items[i].TypeName)
			continue
		}
		items[i].Prices = prices

		appraisal.Totals.Buy += prices.Buy.Max * float64(items[i].Quantity)
		appraisal.Totals.Sell += prices.Sell.Min * float64(items[i].Quantity)
		appraisal.Totals.Volume += prices.All.Volume * items[i].Quantity
	}
	appraisal.Items = items

	return appraisal, nil
}

type AppraisalItem struct {
	Name     string                 `json:"name"`
	TypeID   int64                  `json:"typeID"`
	TypeName string                 `json:"typeName"`
	Quantity int64                  `json:"quantity"`
	Meta     map[string]interface{} `json:"meta"`
	Prices   Prices                 `json:"prices"`
}

func (i AppraisalItem) SellTotal() float64 {
	return float64(i.Quantity) * i.Prices.Sell.Min
}

func (i AppraisalItem) BuyTotal() float64 {
	return float64(i.Quantity) * i.Prices.Sell.Max
}

type Prices struct {
	All  PriceStats `json:"all"`
	Buy  PriceStats `json:"buy"`
	Sell PriceStats `json:"sell"`
}

type PriceStats struct {
	Average    float64 `json:"avg"`
	Max        float64 `json:"max"`
	Median     float64 `json:"median"`
	Min        float64 `json:"min"`
	Percentile float64 `json:"percentile"`
	Stddev     float64 `json:"stddev"`
	Volume     int64   `json:"volume"`
}

func findKind(result parsers.ParserResult) (string, error) {
	largestLines := -1
	largestLinesParser := "unknown"
	switch r := result.(type) {
	default:
		return largestLinesParser, fmt.Errorf("unexpected type %T", r)
	case *parsers.MultiParserResult:
		if len(r.Results) == 0 {
			return largestLinesParser, fmt.Errorf("No valid lines found")
		}
		for _, subResult := range r.Results {
			if len(subResult.Lines()) > largestLines {
				largestLines = len(subResult.Lines())
				largestLinesParser = subResult.Name()
			}
		}
	}
	return largestLinesParser, nil
}

func parserResultToAppraisalItems(result parsers.ParserResult) []AppraisalItem {
	var items []AppraisalItem
	switch r := result.(type) {
	default:
		log.Printf("unexpected type %T", r)
	case *parsers.MultiParserResult:
		for _, subResult := range r.Results {
			items = append(items, parserResultToAppraisalItems(subResult)...)
		}
	case *parsers.AssetList:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.CargoScan:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Contract:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta:     map[string]interface{}{"fitted": item.Fitted},
				})
		}
	case *parsers.DScan:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: 1})
		}
	case *parsers.EFT:
		items = append(items, AppraisalItem{Name: r.Ship, Quantity: 1})
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Fitting:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Industry:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.Killmail:
		for _, item := range r.Dropped {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"dropped":  true,
						"location": item.Location,
					},
				})
		}
		for _, item := range r.Destroyed {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"destroyed": true,
						"location":  item.Location,
					},
				})
		}
	case *parsers.Listing:
		for _, item := range r.Items {
			items = append(items, AppraisalItem{Name: item.Name, Quantity: item.Quantity})
		}
	case *parsers.LootHistory:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"player_name": item.PlayerName,
					},
				})
		}
	case *parsers.PI:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"routed": item.Routed,
						"volume": item.Volume,
					},
				})
		}
	case *parsers.SurveyScan:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"distance": item.Distance,
					},
				})
		}
	case *parsers.ViewContents:
		for _, item := range r.Items {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"location": item.Location,
					},
				})
		}
	case *parsers.Wallet:
		for _, item := range r.ItemizedTransactions {
			items = append(items,
				AppraisalItem{
					Name:     item.Name,
					Quantity: item.Quantity,
					Meta: map[string]interface{}{
						"client":   item.Client,
						"credit":   item.Credit,
						"currency": item.Currency,
						"datetime": item.Datetime,
						"location": item.Location,
						"price":    item.Price,
					},
				})
		}
	}
	return items
}
<|endoftext|>


<commit_before>package filer

import (
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"math/rand"
	"testing"
)

func TestReadResolvedChunks(t *testing.T) {

	chunks := []*filer_pb.FileChunk{
		{
			FileId: "a",
			Offset: 0,
			Size:   100,
			Mtime:  1,
		},
		{
			FileId: "b",
			Offset: 50,
			Size:   100,
			Mtime:  2,
		},
		{
			FileId: "c",
			Offset: 200,
			Size:   50,
			Mtime:  3,
		},
		{
			FileId: "d",
			Offset: 250,
			Size:   50,
			Mtime:  4,
		},
		{
			FileId: "e",
			Offset: 175,
			Size:   100,
			Mtime:  5,
		},
	}

	visibles := readResolvedChunks(chunks)

	for _, visible := range visibles {
		fmt.Printf("[%d,%d) %s %d\n", visible.start, visible.stop, visible.fileId, visible.modifiedTime)
	}

}

func TestRandomizedReadResolvedChunks(t *testing.T) {

	var limit int64 = 1024*1024
	array := make([]int64, limit)
	var chunks []*filer_pb.FileChunk
	for ts := int64(0); ts < 1024; ts++ {
		x := rand.Int63n(limit)
		y := rand.Int63n(limit)
		size := x - y
		if size < 0 {
			size = -size
		}
		if size > 1024 {
			size = 1024
		}
		start := x
		if start > y {
			start = y
		}
		chunks = append(chunks, randomWrite(array, start, size, ts))
	}

	visibles := readResolvedChunks(chunks)

	for _, visible := range visibles {
		for i := visible.start; i<visible.stop;i++{
			if array[i] != visible.modifiedTime {
				t.Errorf("position %d expected ts %d actual ts %d", i, array[i], visible.modifiedTime)
			}
		}
	}

	// fmt.Printf("visibles %d", len(visibles))

}

func randomWrite(array []int64, start int64, size int64, ts int64) *filer_pb.FileChunk {
	for i := start; i < start+size; i++ {
		array[i] = ts
	}
	// fmt.Printf("write [%d,%d) %d\n", start, start+size, ts)
	return &filer_pb.FileChunk{
		FileId: "",
		Offset: start,
		Size:   uint64(size),
		Mtime:  ts,
	}
}

func TestSequentialReadResolvedChunks(t *testing.T) {

	var chunkSize int64 = 1024*1024*2
	var chunks []*filer_pb.FileChunk
	for ts := int64(0); ts < 13; ts++ {
		chunks = append(chunks, &filer_pb.FileChunk{
			FileId: "",
			Offset: chunkSize*ts,
			Size:   uint64(chunkSize),
			Mtime:  1,
		})
	}

	visibles := readResolvedChunks(chunks)

	fmt.Printf("visibles %d", len(visibles))

}
<commit_msg>add tests<commit_after>package filer

import (
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"math/rand"
	"testing"
)

func TestReadResolvedChunks(t *testing.T) {

	chunks := []*filer_pb.FileChunk{
		{
			FileId: "a",
			Offset: 0,
			Size:   100,
			Mtime:  1,
		},
		{
			FileId: "b",
			Offset: 50,
			Size:   100,
			Mtime:  2,
		},
		{
			FileId: "c",
			Offset: 200,
			Size:   50,
			Mtime:  3,
		},
		{
			FileId: "d",
			Offset: 250,
			Size:   50,
			Mtime:  4,
		},
		{
			FileId: "e",
			Offset: 175,
			Size:   100,
			Mtime:  5,
		},
	}

	visibles := readResolvedChunks(chunks)

	for _, visible := range visibles {
		fmt.Printf("[%d,%d) %s %d\n", visible.start, visible.stop, visible.fileId, visible.modifiedTime)
	}

}

func TestRandomizedReadResolvedChunks(t *testing.T) {

	var limit int64 = 1024*1024
	array := make([]int64, limit)
	var chunks []*filer_pb.FileChunk
	for ts := int64(0); ts < 1024; ts++ {
		x := rand.Int63n(limit)
		y := rand.Int63n(limit)
		size := x - y
		if size < 0 {
			size = -size
		}
		if size > 1024 {
			size = 1024
		}
		start := x
		if start > y {
			start = y
		}
		chunks = append(chunks, randomWrite(array, start, size, ts))
	}

	visibles := readResolvedChunks(chunks)

	for _, visible := range visibles {
		for i := visible.start; i<visible.stop;i++{
			if array[i] != visible.modifiedTime {
				t.Errorf("position %d expected ts %d actual ts %d", i, array[i], visible.modifiedTime)
			}
		}
	}

	// fmt.Printf("visibles %d", len(visibles))

}

func randomWrite(array []int64, start int64, size int64, ts int64) *filer_pb.FileChunk {
	for i := start; i < start+size; i++ {
		array[i] = ts
	}
	// fmt.Printf("write [%d,%d) %d\n", start, start+size, ts)
	return &filer_pb.FileChunk{
		FileId: "",
		Offset: start,
		Size:   uint64(size),
		Mtime:  ts,
	}
}

func TestSequentialReadResolvedChunks(t *testing.T) {

	var chunkSize int64 = 1024*1024*2
	var chunks []*filer_pb.FileChunk
	for ts := int64(0); ts < 13; ts++ {
		chunks = append(chunks, &filer_pb.FileChunk{
			FileId: "",
			Offset: chunkSize*ts,
			Size:   uint64(chunkSize),
			Mtime:  1,
		})
	}

	visibles := readResolvedChunks(chunks)

	fmt.Printf("visibles %d", len(visibles))

}

func TestActualReadResolvedChunks(t *testing.T) {

	chunks := []*filer_pb.FileChunk{
		{
			FileId: "5,e7b96fef48",
			Offset: 0,
			Size:   2097152,
			Mtime:  1634447487595823000,
		},
		{
			FileId: "5,e5562640b9",
			Offset: 2097152,
			Size:   2097152,
			Mtime:  1634447487595826000,
		},
		{
			FileId: "5,df033e0fe4",
			Offset: 4194304,
			Size:   2097152,
			Mtime:  1634447487595827000,
		},
		{
			FileId: "7,eb08148a9b",
			Offset: 6291456,
			Size:   2097152,
			Mtime:  1634447487595827000,
		},
		{
			FileId: "7,e0f92d1604",
			Offset: 8388608,
			Size:   2097152,
			Mtime:  1634447487595828000,
		},
		{
			FileId: "7,e33cb63262",
			Offset: 10485760,
			Size:   2097152,
			Mtime:  1634447487595828000,
		},
		{
			FileId: "5,ea98e40e93",
			Offset: 12582912,
			Size:   2097152,
			Mtime:  1634447487595829000,
		},
		{
			FileId: "5,e165661172",
			Offset: 14680064,
			Size:   2097152,
			Mtime:  1634447487595829000,
		},
		{
			FileId: "3,e692097486",
			Offset: 16777216,
			Size:   2097152,
			Mtime:  1634447487595830000,
		},
		{
			FileId: "3,e28e2e3cbd",
			Offset: 18874368,
			Size:   2097152,
			Mtime:  1634447487595830000,
		},
		{
			FileId: "3,e443974d4e",
			Offset: 20971520,
			Size:   2097152,
			Mtime:  1634447487595830000,
		},
		{
			FileId: "2,e815bed597",
			Offset: 23068672,
			Size:   2097152,
			Mtime:  1634447487595831000,
		},
		{
			FileId: "5,e94715199e",
			Offset: 25165824,
			Size:   1974736,
			Mtime:  1634447487595832000,
		},
	}

	visibles := readResolvedChunks(chunks)

	for _, visible := range visibles {
		fmt.Printf("[%d,%d) %s %d\n", visible.start, visible.stop, visible.fileId, visible.modifiedTime)
	}

}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package notice

import (
	"strconv"
	"sync"
	"testing"

	"github.com/stretchr/testify/assert"
)

func init() {
	SetDebug(true)
}

func TestOpenMessage(t *testing.T) {
	OpenMessage(`testUser`, `testType`)
	user, _ := Default().users.GetOk(`testUser`)
	assert.Equal(t, 1, user.Notice.types.Size())
	assert.True(t, user.Notice.types.Has(`testType`))

	_, clientID := OpenClient(`testUser`)
	assert.Equal(t, 1, user.CountClient())

	CloseClient(`testUser`, clientID)
	assert.Equal(t, 0, user.CountClient())
}

func TestSend(t *testing.T) {
	OpenMessage(`testUser`, `testType`)
	_, clientID := OpenClient(`testUser`)
	var wg sync.WaitGroup
	go func() {
		defer wg.Done()
		b, err := RecvJSON(`testUser`, clientID)
		if err != nil {
			t.Error(err)
		}
		println(string(b))
		assert.Equal(t, `{"client_id":"`+clientID+`","id":null,"type":"testType","title":"testTitle","status":1,"content":"testContent","mode":"","progress":null}`, string(b))
	}()
	wg.Add(1)
	Send(`testUser`, NewMessageWithValue(`testType`, `testTitle`, `testContent`).SetClientID(clientID))
	wg.Wait()
}

func TestSend2(t *testing.T) {
	OpenMessage(`testUser2`, `testType`)
	_, clientID := OpenClient(`testUser2`)
	var wg sync.WaitGroup
	go func() {
		var i = 0
		for {
			b, err := RecvJSON(`testUser`, clientID)
			if err != nil {
				t.Error(err)
			}
			println(string(b))
			assert.Equal(t, `{"client_id":"`+clientID+`","id":null,"type":"testType","title":"testTitle","status":1,"content":"testContent_`+strconv.Itoa(i)+`","mode":"","progress":null}`, string(b))
			i++
		}
	}()
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			Send(`testUser`, NewMessageWithValue(`testType`, `testTitle`, `testContent_`+strconv.Itoa(i)).SetClientID(clientID))
		}(i)
	}
	wg.Wait()
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package notice

import (
	"testing"

	"github.com/stretchr/testify/assert"
)

func init() {
	SetDebug(true)
}

func TestOpenMessage(t *testing.T) {
	OpenMessage(`testUser`, `testType`)
	user, _ := Default().users.GetOk(`testUser`)
	assert.Equal(t, 1, user.Notice.types.Size())
	assert.True(t, user.Notice.types.Has(`testType`))

	_, clientID := OpenClient(`testUser`)
	assert.Equal(t, 1, user.CountClient())

	CloseClient(`testUser`, clientID)
	assert.Equal(t, 0, user.CountClient())
}
<|endoftext|>


<commit_before>package dog

import (
	"io/ioutil"

	"github.com/ghodss/yaml"
)

func ParseDogfile(d []byte) (tm TaskMap, err error) {
	var tasks []Task

	err = yaml.Unmarshal(d, &tasks)
	if err != nil {
		return
	}

	tm = make(TaskMap)
	for _, t := range tasks {
		tm[t.Name] = t
	}

	return
}

// LoadDogFile finds a Dogfile in disk, parses YAML and returns a map
func LoadDogFile() (tm TaskMap, err error) {
	var dat []byte

	dat, err = ioutil.ReadFile("Dogfile.yml")
	if err != nil {
		return
	}

	tm, err = ParseDogfile(dat)
	if err != nil {
		return
	}

	return
}
<commit_msg>Don't allow duplicated task names<commit_after>package dog

import (
	"io/ioutil"

	"github.com/ghodss/yaml"
)

func ParseDogfile(d []byte) (tm TaskMap, err error) {
	var tasks []Task

	err = yaml.Unmarshal(d, &tasks)
	if err != nil {
		return
	}

	tm = make(TaskMap)
	for _, t := range tasks {
		if _, ok := tm[t.Name]; ok {
			// TODO (duplicated task name) fail and return a non-nil error
		} else {
			tm[t.Name] = t
		}
	}

	return
}

// LoadDogFile finds a Dogfile in disk, parses YAML and returns a map
func LoadDogFile() (tm TaskMap, err error) {
	var dat []byte

	dat, err = ioutil.ReadFile("Dogfile.yml")
	if err != nil {
		return
	}

	tm, err = ParseDogfile(dat)
	if err != nil {
		return
	}

	return
}
<|endoftext|>