<commit_before>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"fmt"
	"github.com/limetext/lime/backend/textmate"
	"github.com/limetext/lime/backend/util"
	. "github.com/quarnster/util/text"
	"io/ioutil"
	"math/rand"
	"reflect"
	"testing"
	"time"
)

func TestView(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
	)
	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	v.selection.Clear()
	r := []Region{
		{0, 0},
		{1, 1},
		{2, 2},
		{3, 3},
	}
	for _, r2 := range r {
		v.selection.Add(r2)
	}

	edit = v.BeginEdit()
	for _, ins := range "1234" {
		for i := 0; i < v.selection.Len(); i++ {
			v.Insert(edit, v.selection.Get(i).Begin(), string(ins))
		}
	}
	v.EndEdit(edit)

	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error(d)
	}
	v.undoStack.Undo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "abcd" {
		t.Error("expected 'abcd', but got: ", d)
	}
	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error("expected '1234a1234b1234c1234d', but got: ", d)
	}

	v.selection.Clear()
	r = []Region{
		{0, 0},
		{5, 5},
		{10, 10},
		{15, 15},
	}
	for _, r2 := range r {
		v.selection.Add(r2)
	}

	edit = v.BeginEdit()
	for _, ins := range []string{"hello ", "world"} {
		for i := 0; i < v.selection.Len(); i++ {
			v.Insert(edit, v.selection.Get(i).Begin(), ins)
		}
	}
	v.EndEdit(edit)

	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "hello world1234ahello world1234bhello world1234chello world1234d" {
		t.Error(d)
	}
	v.undoStack.Undo(true)

	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error("expected '1234a1234b1234c1234d', but got: ", d)
	}
	v.undoStack.Undo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "abcd" {
		t.Error("expected 'abcd', but got: ", d)
	}
	v.undoStack.Undo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "" {
		t.Error("expected '', but got: ", d)
	}
	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "abcd" {
		t.Error("expected 'abcd', but got: ", d)
	}

	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error("expected '1234a1234b1234c1234d', but got: ", d)
	}

	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "hello world1234ahello world1234bhello world1234chello world1234d" {
		t.Error(d)
	}
}

func TestErase(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
		s = v.Sel()
	)
	edit := v.BeginEdit()
	v.Insert(edit, 0, "1234abcd5678abcd")
	v.EndEdit(edit)
	s.Clear()
	v.Sel().Add(Region{4, 8})
	v.Sel().Add(Region{12, 16})

	edit = v.BeginEdit()
	for i := 0; i < s.Len(); i++ {
		v.Erase(edit, s.Get(i))
	}
	v.EndEdit(edit)
	if !reflect.DeepEqual(s.Regions(), []Region{{4, 4}, {8, 8}}) {
		t.Error(s)
	}
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "12345678" {
		t.Error(d)
	}
}

// This is not 100% what ST3 does
func TestExtractScope(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in      = "textmate/testdata/main.go"
		expfile = "testdata/scoperange.res"
		syntax  = "textmate/testdata/Go.tmLanguage"
	)
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		t.Fatal(err)
	} else {
		//		v.rootNode = nil
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		last := Region{-1, -1}
		str := ""
		nr := Region{0, 0}
		for v.ExtractScope(1) == nr {
			time.Sleep(time.Millisecond)
		}
		for i := 0; i < v.buffer.Size(); i++ {
			if r := v.ExtractScope(i); r != last {
				str += fmt.Sprintf("%d (%d, %d)\n", i, r.A, r.B)
				last = r
			}
		}
		if d, err := ioutil.ReadFile(expfile); err != nil {
			if err := ioutil.WriteFile(expfile, []byte(str), 0644); err != nil {
				t.Error(err)
			}
		} else if diff := util.Diff(string(d), str); diff != "" {
			t.Error(diff)
		}
	}
}

// This is not 100% what ST3 does, but IMO ST3 is wrong
func TestScopeName(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in      = "textmate/testdata/main.go"
		expfile = "testdata/scopename.res"
		syntax  = "textmate/testdata/Go.tmLanguage"
	)
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		t.Fatal(err)
	} else {
		//		v.rootNode = nil
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		last := ""
		str := ""
		lasti := 0
		for v.ScopeName(1) == "" {
			time.Sleep(250 * time.Millisecond)
		}
		for i := 0; i < v.buffer.Size(); i++ {
			if name := v.ScopeName(i); name != last {
				if last != "" {
					str += fmt.Sprintf("%d-%d: %s\n", lasti, i, last)
					lasti = i
				}
				last = name
			}
		}
		if i := v.Buffer().Size(); lasti != i {
			str += fmt.Sprintf("%d-%d: %s\n", lasti, i, last)
		}
		if d, err := ioutil.ReadFile(expfile); err != nil {
			if err := ioutil.WriteFile(expfile, []byte(str), 0644); err != nil {
				t.Error(err)
			}
		} else if diff := util.Diff(string(d), str); diff != "" {
			t.Error(diff)
		}

	}
}

func TestStress(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	var (
		ed = GetEditor()
		w  = ed.NewWindow()
		v  = w.OpenFile("../frontend/termbox/main.go", 0)
	)
	syntax := "../3rdparty/bundles/go.tmbundle/Syntaxes/Go.tmLanguage"
	v.Settings().Set("syntax", syntax)
	for i := 0; i < 1000; i++ {
		e := v.BeginEdit()
		for i := 0; i < 100; i++ {
			v.Insert(e, 0, "h")
		}
		for i := 0; i < 100; i++ {
			v.Erase(e, Region{0, 1})
		}
		v.EndEdit(e)
	}
}

func TestTransform(t *testing.T) {
	sc, err := textmate.LoadTheme("../3rdparty/bundles/TextMate-Themes/GlitterBomb.tmTheme")
	if err != nil {
		t.Fatal(err)
	}
	var (
		w Window
		v = w.NewFile()
	)
	v.Settings().Set("syntax", "textmate/testdata/Go.tmLanguage")

	d, err := ioutil.ReadFile("view.go")
	if err != nil {
		t.Fatal(err)
	}
	e := v.BeginEdit()
	v.Insert(e, 0, string(d))
	v.EndEdit(e)

	time.Sleep(time.Second)
	a := v.Transform(sc, Region{0, 100}).Transcribe()
	v.Transform(sc, Region{100, 200}).Transcribe()
	c := v.Transform(sc, Region{0, 100}).Transcribe()
	if !reflect.DeepEqual(a, c) {
		t.Errorf("not equal:\n%v\n%v", a, c)
	}
}

func BenchmarkScopeNameLinear(b *testing.B) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in     = "textmate/language_test.go"
		syntax = "textmate/testdata/Go.tmLanguage"
	)
	b.StopTimer()
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		b.Fatal(err)
	} else {
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		b.StartTimer()
		for j := 0; j < b.N; j++ {
			for i := 0; i < v.buffer.Size(); i++ {
				v.ScopeName(i)
			}
		}
	}
}

func BenchmarkScopeNameRandom(b *testing.B) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in     = "textmate/language_test.go"
		syntax = "textmate/testdata/Go.tmLanguage"
	)
	b.StopTimer()
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		b.Fatal(err)
	} else {
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		p := rand.Perm(b.N)
		b.StartTimer()
		for _, i := range p {
			v.ScopeName(i)
		}
	}
}
<commit_msg>atomic save not working<commit_after>// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.

package backend

import (
	"fmt"
	"github.com/limetext/lime/backend/textmate"
	"github.com/limetext/lime/backend/util"
	. "github.com/quarnster/util/text"
	"io/ioutil"
	"math/rand"
	"reflect"
	"testing"
	"time"
)

func TestView(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
	)
	edit := v.BeginEdit()
	v.Insert(edit, 0, "abcd")
	v.EndEdit(edit)
	v.selection.Clear()
	r := []Region{
		{0, 0},
		{1, 1},
		{2, 2},
		{3, 3},
	}
	for _, r2 := range r {
		v.selection.Add(r2)
	}

	edit = v.BeginEdit()
	for _, ins := range "1234" {
		for i := 0; i < v.selection.Len(); i++ {
			v.Insert(edit, v.selection.Get(i).Begin(), string(ins))
		}
	}
	v.EndEdit(edit)

	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error(d)
	}
	v.undoStack.Undo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "abcd" {
		t.Error("expected 'abcd', but got: ", d)
	}
	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error("expected '1234a1234b1234c1234d', but got: ", d)
	}

	v.selection.Clear()
	r = []Region{
		{0, 0},
		{5, 5},
		{10, 10},
		{15, 15},
	}
	for _, r2 := range r {
		v.selection.Add(r2)
	}

	edit = v.BeginEdit()
	for _, ins := range []string{"hello ", "world"} {
		for i := 0; i < v.selection.Len(); i++ {
			v.Insert(edit, v.selection.Get(i).Begin(), ins)
		}
	}
	v.EndEdit(edit)

	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "hello world1234ahello world1234bhello world1234chello world1234d" {
		t.Error(d)
	}
	v.undoStack.Undo(true)

	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error("expected '1234a1234b1234c1234d', but got: ", d)
	}
	v.undoStack.Undo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "abcd" {
		t.Error("expected 'abcd', but got: ", d)
	}
	v.undoStack.Undo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "" {
		t.Error("expected '', but got: ", d)
	}
	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "abcd" {
		t.Error("expected 'abcd', but got: ", d)
	}

	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "1234a1234b1234c1234d" {
		t.Error("expected '1234a1234b1234c1234d', but got: ", d)
	}

	v.undoStack.Redo(true)
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "hello world1234ahello world1234bhello world1234chello world1234d" {
		t.Error(d)
	}
}

func TestErase(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
		s = v.Sel()
	)
	edit := v.BeginEdit()
	v.Insert(edit, 0, "1234abcd5678abcd")
	v.EndEdit(edit)
	s.Clear()
	v.Sel().Add(Region{4, 8})
	v.Sel().Add(Region{12, 16})

	edit = v.BeginEdit()
	for i := 0; i < s.Len(); i++ {
		v.Erase(edit, s.Get(i))
	}
	v.EndEdit(edit)
	if !reflect.DeepEqual(s.Regions(), []Region{{4, 4}, {8, 8}}) {
		t.Error(s)
	}
	if d := v.buffer.Substr(Region{0, v.buffer.Size()}); d != "12345678" {
		t.Error(d)
	}
}

// This is not 100% what ST3 does
func TestExtractScope(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in      = "textmate/testdata/main.go"
		expfile = "testdata/scoperange.res"
		syntax  = "textmate/testdata/Go.tmLanguage"
	)
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		t.Fatal(err)
	} else {
		//		v.rootNode = nil
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		last := Region{-1, -1}
		str := ""
		nr := Region{0, 0}
		for v.ExtractScope(1) == nr {
			time.Sleep(time.Millisecond)
		}
		for i := 0; i < v.buffer.Size(); i++ {
			if r := v.ExtractScope(i); r != last {
				str += fmt.Sprintf("%d (%d, %d)\n", i, r.A, r.B)
				last = r
			}
		}
		if d, err := ioutil.ReadFile(expfile); err != nil {
			if err := ioutil.WriteFile(expfile, []byte(str), 0644); err != nil {
				t.Error(err)
			}
		} else if diff := util.Diff(string(d), str); diff != "" {
			t.Error(diff)
		}
	}
}

// This is not 100% what ST3 does, but IMO ST3 is wrong
func TestScopeName(t *testing.T) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in      = "textmate/testdata/main.go"
		expfile = "testdata/scopename.res"
		syntax  = "textmate/testdata/Go.tmLanguage"
	)
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		t.Fatal(err)
	} else {
		//		v.rootNode = nil
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		last := ""
		str := ""
		lasti := 0
		for v.ScopeName(1) == "" {
			time.Sleep(250 * time.Millisecond)
		}
		for i := 0; i < v.buffer.Size(); i++ {
			if name := v.ScopeName(i); name != last {
				if last != "" {
					str += fmt.Sprintf("%d-%d: %s\n", lasti, i, last)
					lasti = i
				}
				last = name
			}
		}
		if i := v.Buffer().Size(); lasti != i {
			str += fmt.Sprintf("%d-%d: %s\n", lasti, i, last)
		}
		if d, err := ioutil.ReadFile(expfile); err != nil {
			if err := ioutil.WriteFile(expfile, []byte(str), 0644); err != nil {
				t.Error(err)
			}
		} else if diff := util.Diff(string(d), str); diff != "" {
			t.Error(diff)
		}

	}
}

func TestStress(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	var (
		ed = GetEditor()
		w  = ed.NewWindow()
		v  = w.OpenFile("../frontend/termbox/main.go", 0)
	)
	syntax := "../3rdparty/bundles/go.tmbundle/Syntaxes/Go.tmLanguage"
	v.Settings().Set("syntax", syntax)
	for i := 0; i < 1000; i++ {
		e := v.BeginEdit()
		for i := 0; i < 100; i++ {
			v.Insert(e, 0, "h")
		}
		for i := 0; i < 100; i++ {
			v.Erase(e, Region{0, 1})
		}
		v.EndEdit(e)
	}
}

func TestTransform(t *testing.T) {
	sc, err := textmate.LoadTheme("../3rdparty/bundles/TextMate-Themes/GlitterBomb.tmTheme")
	if err != nil {
		t.Fatal(err)
	}
	var (
		w Window
		v = w.NewFile()
	)
	v.Settings().Set("syntax", "textmate/testdata/Go.tmLanguage")

	d, err := ioutil.ReadFile("view.go")
	if err != nil {
		t.Fatal(err)
	}
	e := v.BeginEdit()
	v.Insert(e, 0, string(d))
	v.EndEdit(e)

	time.Sleep(time.Second)
	a := v.Transform(sc, Region{0, 100}).Transcribe()
	v.Transform(sc, Region{100, 200}).Transcribe()
	c := v.Transform(sc, Region{0, 100}).Transcribe()
	if !reflect.DeepEqual(a, c) {
		t.Errorf("not equal:\n%v\n%v", a, c)
	}
}

func TestSaveAs(t *testing.T) {
	test := struct {
		data []byte
		file string
	}{
		[]byte("abc"),
		"testdata/test",
	}
	var (
		w Window
		v = w.NewFile()
		e = v.BeginEdit()
	)
	v.Insert(e, 0, string(test.data))
	v.EndEdit(e)
	err := v.SaveAs(test.file)
	if err != nil {
		t.Fatalf("Can't save to `%s`: %s", test.file, err)
	}
	data, err := ioutil.ReadFile(test.file)
	if err != nil {
		t.Fatalf("Can't read `%s`: %s", test.file, err)
	}
	if string(data) != string(test.data) {
		t.Errorf("Expected `%s` contain %v, but got %s", test.file, test.data, data)
	}
}

func BenchmarkScopeNameLinear(b *testing.B) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in     = "textmate/language_test.go"
		syntax = "textmate/testdata/Go.tmLanguage"
	)
	b.StopTimer()
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		b.Fatal(err)
	} else {
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		b.StartTimer()
		for j := 0; j < b.N; j++ {
			for i := 0; i < v.buffer.Size(); i++ {
				v.ScopeName(i)
			}
		}
	}
}

func BenchmarkScopeNameRandom(b *testing.B) {
	var (
		w Window
		v = w.NewFile()
	)
	const (
		in     = "textmate/language_test.go"
		syntax = "textmate/testdata/Go.tmLanguage"
	)
	b.StopTimer()
	v.Settings().Set("syntax", syntax)
	if d, err := ioutil.ReadFile(in); err != nil {
		b.Fatal(err)
	} else {
		e := v.BeginEdit()
		v.Insert(e, 0, string(d))
		v.EndEdit(e)
		p := rand.Perm(b.N)
		b.StartTimer()
		for _, i := range p {
			v.ScopeName(i)
		}
	}
}
<|endoftext|>


<commit_before>package services

import (
	jcontext "context"
	"fmt"
	"hash/crc32"
	"net/http"
	"strings"
	"time"

	"github.com/JREAMLU/core/com"
	"github.com/JREAMLU/core/db/mysql"
	"github.com/JREAMLU/core/global"
	io "github.com/JREAMLU/core/inout"
	"github.com/JREAMLU/jkernel/base/models/mentity"
	"github.com/JREAMLU/jkernel/base/models/mmysql"
	"github.com/JREAMLU/jkernel/base/models/mredis"
	"github.com/JREAMLU/jkernel/base/services/atom"
	"github.com/JREAMLU/jkernel/base/services/entity"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/validation"
	"github.com/beego/i18n"
	"github.com/pquerna/ffjson/ffjson"
)

type Url struct {
	Data struct {
		Urls []struct {
			LongURL string `json:"long_url" valid:"Required"`
			IP      string `json:"ip" valid:"IP"`
		} `json:"urls" valid:"Required"`
	} `json:"data" valid:"Required"`
}

type UrlExpand struct {
	Shorten []string `json:"shorten" valid:"Required"`
}

var ip string

func GetParams(url Url) Url {
	return url
}

func (r *Url) Valid(v *validation.Validation) {}

func (r *Url) GoShorten(jctx jcontext.Context, data map[string]interface{}) (httpStatus int, output io.Output) {
	ffjson.Unmarshal(data["body"].([]byte), r)
	ip = data["headermap"].(http.Header)["X-Forwarded-For"][0]
	ch, err := io.InputParamsCheck(jctx, data, &r.Data)
	if err != nil {
		return http.StatusExpectationFailed, io.Fail(ch.Message, "DATAPARAMSILLEGAL", jctx.Value("requestID").(string))
	}

	list, err := shorten(jctx, r)
	if err != nil {
		beego.Info(jctx.Value("requestID").(string), ":", "shorten error: ", err)
		return http.StatusExpectationFailed, io.Fail(i18n.Tr(global.Lang, "url.SHORTENILLEGAL"), "DATAPARAMSILLEGAL", jctx.Value("requestID").(string))
	}

	var datalist entity.DataList
	datalist.List = list
	datalist.Total = len(list)

	return http.StatusCreated, io.Suc(datalist, ch.RequestID)
}

func shorten(jctx jcontext.Context, r *Url) (map[string]interface{}, error) {
	redirects := getRedirects(r)
	list, err := setDB(redirects)
	if err != nil {
		return nil, err
	}
	return list, nil
}

func setDB(redirects []mentity.Redirect) (map[string]interface{}, error) {
	var longUrls []interface{}
	for _, v := range redirects {
		longUrls = append(longUrls, v.LongUrl)
	}
	shortens, err := mredis.ShortenHMGet(longUrls)
	if err != nil {
		return nil, err
	}
	var notExistShortenSlice []interface{}
	var existShortenMap = make(map[string]interface{})
	var notExistShortenMap = make(map[string]interface{})
	for k, v := range shortens {
		if v == "" {
			notExistShortenSlice = append(notExistShortenSlice, k)
		} else {
			existShortenMap[k] = v
		}
	}
	var notExistRedirectList []mentity.Redirect
	var notExistRedirectListCache []interface{}
	//TODO redis没有 去mysql查 mysql存在的就插redis
	for _, v := range redirects {
		for _, v1 := range notExistShortenSlice {
			if v.LongUrl == v1 {
				notExistRedirectList = append(notExistRedirectList, v)
				notExistRedirectListCache = append(notExistRedirectListCache, v.LongUrl)
				notExistRedirectListCache = append(notExistRedirectListCache, v.ShortUrl)
				notExistShortenMap[v.LongUrl] = v.ShortUrl
			}
		}
	}
	if len(notExistRedirectList) > 0 {
		//mysql batch
		tx := mysql.X.Begin()
		err = mmysql.ShortenInBatch(notExistRedirectList, tx)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
		//批量插redis
		_, err = mredis.ShortenHMSet(notExistRedirectListCache)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
		tx.Commit()
	}
	shortenMap := com.MapMerge(existShortenMap, notExistShortenMap)
	fmt.Println("<<<<", shortenMap)

	return shortenMap, nil
}

func getRedirects(r *Url) []mentity.Redirect {
	var redirects []mentity.Redirect
	for _, val := range r.Data.Urls {
		shortUrl := atom.GetShortenUrl(val.LongURL)
		var redirect mentity.Redirect
		redirect.LongUrl = val.LongURL
		redirect.ShortUrl = shortUrl
		redirect.LongCrc = uint64(crc32.ChecksumIEEE([]byte(val.LongURL)))
		redirect.ShortCrc = uint64(crc32.ChecksumIEEE([]byte(shortUrl)))
		redirect.Status = 1
		redirect.CreatedByIP = uint64(com.Ip2Int(ip))
		redirect.UpdateByIP = uint64(com.Ip2Int(ip))
		redirect.CreateAT = uint64(time.Now().Unix())
		redirect.UpdateAT = uint64(time.Now().Unix())
		redirects = append(redirects, redirect)
	}
	return redirects
}

// func shorten(jctx jcontext.Context, r *Url) map[string]interface{} {
// 	list := make(map[string]interface{})
//
// 	for _, val := range r.Data.Urls {
// 		shortUrl := atom.GetShortenUrl(val.LongURL)
//
// 		short, err := setDB(val.LongURL, shortUrl)
// 		if err != nil {
// 			beego.Info(jctx.Value("requestID").(string), ":", "setDB error: ", err)
// 		}
//
// 		list[val.LongURL] = beego.AppConfig.String("ShortenDomain") + short
// 	}
//
// 	return list
// }
//
// func setDB(origin string, short string) (string, error) {
// 	reply, err := mredis.ShortenHGet(origin)
// 	if err != nil && err.Error() != "redigo: nil returned" {
// 		return "", err
// 	}
// 	if reply == "" {
// 		var redirect mentity.Redirect
// 		redirect.LongUrl = origin
// 		redirect.ShortUrl = short
// 		redirect.LongCrc = uint64(crc32.ChecksumIEEE([]byte(origin)))
// 		redirect.ShortCrc = uint64(crc32.ChecksumIEEE([]byte(short)))
// 		redirect.Status = 1
// 		redirect.CreatedByIP = uint64(com.Ip2Int(ip))
// 		redirect.UpdateByIP = uint64(com.Ip2Int(ip))
// 		redirect.CreateAT = uint64(time.Now().Unix())
// 		redirect.UpdateAT = uint64(time.Now().Unix())
//
// 		_, err := mmysql.ShortenIn(redirect)
// 		if err != nil {
// 			return "", err
// 		}
// 		_, err = mredis.ShortenHSet(origin, short)
// 		if err != nil {
// 			return "", err
// 		}
// 		_, err = mredis.ExpandHSet(short, origin)
// 		if err != nil {
// 			return "", err
// 		}
//
// 		return short, nil
// 	}
// 	return reply, nil
// }

func (r *Url) GoExpand(jctx jcontext.Context, data map[string]interface{}) (httpStatus int, output io.Output) {
	var ue UrlExpand
	ffjson.Unmarshal([]byte(data["querystrjson"].(string)), &ue)

	ch, err := io.InputParamsCheck(jctx, data, ue)
	if err != nil {
		return http.StatusExpectationFailed, io.Fail(ch.Message, "DATAPARAMSILLEGAL", jctx.Value("requestID").(string))
	}

	list := expand(jctx, &ue)

	var datalist entity.DataList
	datalist.List = list
	datalist.Total = len(list)

	return http.StatusCreated, io.Suc(datalist, ch.RequestID)
}

func expand(jctx jcontext.Context, ue *UrlExpand) map[string]interface{} {
	list := make(map[string]interface{})
	shortens := ue.Shorten[0]
	for _, shorten := range strings.Split(shortens, ",") {
		reply, err := mredis.ExpandHGet(shorten)
		if err != nil {
			beego.Info(jctx.Value("requestID").(string), ":", "expand error: ", err)
		}
		atom.Mu.Lock()
		list[shorten] = reply
		atom.Mu.Unlock()
	}
	return list
}
<commit_msg>add todo<commit_after>package services

import (
	jcontext "context"
	"fmt"
	"hash/crc32"
	"net/http"
	"strings"
	"time"

	"github.com/JREAMLU/core/com"
	"github.com/JREAMLU/core/db/mysql"
	"github.com/JREAMLU/core/global"
	io "github.com/JREAMLU/core/inout"
	"github.com/JREAMLU/jkernel/base/models/mentity"
	"github.com/JREAMLU/jkernel/base/models/mmysql"
	"github.com/JREAMLU/jkernel/base/models/mredis"
	"github.com/JREAMLU/jkernel/base/services/atom"
	"github.com/JREAMLU/jkernel/base/services/entity"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/validation"
	"github.com/beego/i18n"
	"github.com/pquerna/ffjson/ffjson"
)

type Url struct {
	Data struct {
		Urls []struct {
			LongURL string `json:"long_url" valid:"Required"`
			IP      string `json:"ip" valid:"IP"`
		} `json:"urls" valid:"Required"`
	} `json:"data" valid:"Required"`
}

type UrlExpand struct {
	Shorten []string `json:"shorten" valid:"Required"`
}

var ip string

func GetParams(url Url) Url {
	return url
}

func (r *Url) Valid(v *validation.Validation) {}

func (r *Url) GoShorten(jctx jcontext.Context, data map[string]interface{}) (httpStatus int, output io.Output) {
	ffjson.Unmarshal(data["body"].([]byte), r)
	ip = data["headermap"].(http.Header)["X-Forwarded-For"][0]
	ch, err := io.InputParamsCheck(jctx, data, &r.Data)
	if err != nil {
		return http.StatusExpectationFailed, io.Fail(ch.Message, "DATAPARAMSILLEGAL", jctx.Value("requestID").(string))
	}

	list, err := shorten(jctx, r)
	if err != nil {
		beego.Info(jctx.Value("requestID").(string), ":", "shorten error: ", err)
		return http.StatusExpectationFailed, io.Fail(i18n.Tr(global.Lang, "url.SHORTENILLEGAL"), "DATAPARAMSILLEGAL", jctx.Value("requestID").(string))
	}

	var datalist entity.DataList
	datalist.List = list
	datalist.Total = len(list)

	return http.StatusCreated, io.Suc(datalist, ch.RequestID)
}

func shorten(jctx jcontext.Context, r *Url) (map[string]interface{}, error) {
	redirects := getRedirects(r)
	list, err := setDB(redirects)
	if err != nil {
		return nil, err
	}
	return list, nil
}

func setDB(redirects []mentity.Redirect) (map[string]interface{}, error) {
	//TODO 增加条数限制
	var longUrls []interface{}
	for _, v := range redirects {
		longUrls = append(longUrls, v.LongUrl)
	}
	shortens, err := mredis.ShortenHMGet(longUrls)
	if err != nil {
		return nil, err
	}
	var notExistShortenSlice []interface{}
	var existShortenMap = make(map[string]interface{})
	var notExistShortenMap = make(map[string]interface{})
	for k, v := range shortens {
		if v == "" {
			notExistShortenSlice = append(notExistShortenSlice, k)
		} else {
			existShortenMap[k] = v
		}
	}
	var notExistRedirectList []mentity.Redirect
	var notExistRedirectListCache []interface{}
	//TODO redis没有 去mysql查 mysql存在的就插redis
	for _, v := range redirects {
		for _, v1 := range notExistShortenSlice {
			if v.LongUrl == v1 {
				notExistRedirectList = append(notExistRedirectList, v)
				notExistRedirectListCache = append(notExistRedirectListCache, v.LongUrl)
				notExistRedirectListCache = append(notExistRedirectListCache, v.ShortUrl)
				notExistShortenMap[v.LongUrl] = v.ShortUrl
			}
		}
	}
	if len(notExistRedirectList) > 0 {
		//mysql batch
		tx := mysql.X.Begin()
		err = mmysql.ShortenInBatch(notExistRedirectList, tx)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
		//批量插redis
		_, err = mredis.ShortenHMSet(notExistRedirectListCache)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
		tx.Commit()
	}
	shortenMap := com.MapMerge(existShortenMap, notExistShortenMap)
	fmt.Println("<<<<", shortenMap)

	return shortenMap, nil
}

func getRedirects(r *Url) []mentity.Redirect {
	var redirects []mentity.Redirect
	for _, val := range r.Data.Urls {
		shortUrl := atom.GetShortenUrl(val.LongURL)
		var redirect mentity.Redirect
		redirect.LongUrl = val.LongURL
		redirect.ShortUrl = shortUrl
		redirect.LongCrc = uint64(crc32.ChecksumIEEE([]byte(val.LongURL)))
		redirect.ShortCrc = uint64(crc32.ChecksumIEEE([]byte(shortUrl)))
		redirect.Status = 1
		redirect.CreatedByIP = uint64(com.Ip2Int(ip))
		redirect.UpdateByIP = uint64(com.Ip2Int(ip))
		redirect.CreateAT = uint64(time.Now().Unix())
		redirect.UpdateAT = uint64(time.Now().Unix())
		redirects = append(redirects, redirect)
	}
	return redirects
}

// func shorten(jctx jcontext.Context, r *Url) map[string]interface{} {
// 	list := make(map[string]interface{})
//
// 	for _, val := range r.Data.Urls {
// 		shortUrl := atom.GetShortenUrl(val.LongURL)
//
// 		short, err := setDB(val.LongURL, shortUrl)
// 		if err != nil {
// 			beego.Info(jctx.Value("requestID").(string), ":", "setDB error: ", err)
// 		}
//
// 		list[val.LongURL] = beego.AppConfig.String("ShortenDomain") + short
// 	}
//
// 	return list
// }
//
// func setDB(origin string, short string) (string, error) {
// 	reply, err := mredis.ShortenHGet(origin)
// 	if err != nil && err.Error() != "redigo: nil returned" {
// 		return "", err
// 	}
// 	if reply == "" {
// 		var redirect mentity.Redirect
// 		redirect.LongUrl = origin
// 		redirect.ShortUrl = short
// 		redirect.LongCrc = uint64(crc32.ChecksumIEEE([]byte(origin)))
// 		redirect.ShortCrc = uint64(crc32.ChecksumIEEE([]byte(short)))
// 		redirect.Status = 1
// 		redirect.CreatedByIP = uint64(com.Ip2Int(ip))
// 		redirect.UpdateByIP = uint64(com.Ip2Int(ip))
// 		redirect.CreateAT = uint64(time.Now().Unix())
// 		redirect.UpdateAT = uint64(time.Now().Unix())
//
// 		_, err := mmysql.ShortenIn(redirect)
// 		if err != nil {
// 			return "", err
// 		}
// 		_, err = mredis.ShortenHSet(origin, short)
// 		if err != nil {
// 			return "", err
// 		}
// 		_, err = mredis.ExpandHSet(short, origin)
// 		if err != nil {
// 			return "", err
// 		}
//
// 		return short, nil
// 	}
// 	return reply, nil
// }

func (r *Url) GoExpand(jctx jcontext.Context, data map[string]interface{}) (httpStatus int, output io.Output) {
	var ue UrlExpand
	ffjson.Unmarshal([]byte(data["querystrjson"].(string)), &ue)

	ch, err := io.InputParamsCheck(jctx, data, ue)
	if err != nil {
		return http.StatusExpectationFailed, io.Fail(ch.Message, "DATAPARAMSILLEGAL", jctx.Value("requestID").(string))
	}

	list := expand(jctx, &ue)

	var datalist entity.DataList
	datalist.List = list
	datalist.Total = len(list)

	return http.StatusCreated, io.Suc(datalist, ch.RequestID)
}

func expand(jctx jcontext.Context, ue *UrlExpand) map[string]interface{} {
	list := make(map[string]interface{})
	shortens := ue.Shorten[0]
	for _, shorten := range strings.Split(shortens, ",") {
		reply, err := mredis.ExpandHGet(shorten)
		if err != nil {
			beego.Info(jctx.Value("requestID").(string), ":", "expand error: ", err)
		}
		atom.Mu.Lock()
		list[shorten] = reply
		atom.Mu.Unlock()
	}
	return list
}
<|endoftext|>


<commit_before>package HumorChecker // "cirello.io/HumorChecker"

import (
	"bufio"
	"regexp"
	"strings"
)

type sign int

const (
	positive sign = +1
	negative sign = -1
)

//Score is the result of sentiment calculation
type Score struct {
	// Score is the sum of the sentiment points of the analyzed text.
	// Negativity will render negative points only, and vice-versa.
	Score float64

	// Comparative establishes a ratio of sentiment per word
	Comparative float64

	// List of words for a given sentiment.
	Words []string
}

//FullScore is the difference between positive and negative sentiment
type FullScore struct {
	// Score is the difference between positive and negative sentiment
	// scores.
	Score float64

	// Comparative is the difference between positive and negative sentiment
	// comparative scores.
	Comparative float64

	// Positive score object
	Positive Score

	// Negative score object
	Negative Score
}

var lettersAndSpaceOnly = regexp.MustCompile(`[^a-zA-Z ]+`)

func calculateScore(phrase string, calcSign sign) Score {
	var hits float64
	var words []string
	var count int

	var lettersAndSpaceOnly = regexp.MustCompile(`[^a-zA-Z ]+`)
	scanner := bufio.NewScanner(strings.NewReader(strings.ToLower(lettersAndSpaceOnly.ReplaceAllString(phrase, " "))))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		count++
		word := scanner.Text()
		if v, ok := afinn[word]; ok {
			if (calcSign == positive && v > 0) || (calcSign == negative && v < 0) {
				hits += v * float64(calcSign)
				words = append(words, word)
			}
		}
	}

	return Score{
		Score:       hits,
		Comparative: hits / float64(count),
		Words:       words,
	}
}

// Negativity calculates the negative sentiment of a sentence
func Negativity(phrase string) Score {
	return calculateScore(phrase, negative)
}

// Positivity calculates the positive sentiment of a sentence
func Positivity(phrase string) Score {
	return calculateScore(phrase, positive)
}

// Analyze calculates overall sentiment
func Analyze(phrase string) FullScore {
	pos := Positivity(phrase)
	neg := Negativity(phrase)

	return FullScore{
		Score:       pos.Score - neg.Score,
		Comparative: pos.Comparative - neg.Comparative,
		Positive:    pos,
		Negative:    neg,
	}
}
<commit_msg>Removed global lettersAndSpaceOnly<commit_after>package HumorChecker // "cirello.io/HumorChecker"

import (
	"bufio"
	"regexp"
	"strings"
)

type sign int

const (
	positive sign = +1
	negative sign = -1
)

//Score is the result of sentiment calculation
type Score struct {
	// Score is the sum of the sentiment points of the analyzed text.
	// Negativity will render negative points only, and vice-versa.
	Score float64

	// Comparative establishes a ratio of sentiment per word
	Comparative float64

	// List of words for a given sentiment.
	Words []string
}

//FullScore is the difference between positive and negative sentiment
type FullScore struct {
	// Score is the difference between positive and negative sentiment
	// scores.
	Score float64

	// Comparative is the difference between positive and negative sentiment
	// comparative scores.
	Comparative float64

	// Positive score object
	Positive Score

	// Negative score object
	Negative Score
}

func calculateScore(phrase string, calcSign sign) Score {
	var hits float64
	var words []string
	var count int
	var lettersAndSpaceOnly = regexp.MustCompile(`[^a-zA-Z ]+`)

	scanner := bufio.NewScanner(strings.NewReader(strings.ToLower(lettersAndSpaceOnly.ReplaceAllString(phrase, " "))))
	scanner.Split(bufio.ScanWords)
	for scanner.Scan() {
		count++
		word := scanner.Text()
		if v, ok := afinn[word]; ok {
			if (calcSign == positive && v > 0) || (calcSign == negative && v < 0) {
				hits += v * float64(calcSign)
				words = append(words, word)
			}
		}
	}

	return Score{
		Score:       hits,
		Comparative: hits / float64(count),
		Words:       words,
	}
}

// Negativity calculates the negative sentiment of a sentence
func Negativity(phrase string) Score {
	return calculateScore(phrase, negative)
}

// Positivity calculates the positive sentiment of a sentence
func Positivity(phrase string) Score {
	return calculateScore(phrase, positive)
}

// Analyze calculates overall sentiment
func Analyze(phrase string) FullScore {
	pos := Positivity(phrase)
	neg := Negativity(phrase)

	return FullScore{
		Score:       pos.Score - neg.Score,
		Comparative: pos.Comparative - neg.Comparative,
		Positive:    pos,
		Negative:    neg,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"strconv"

	"github.com/kataras/iris"
)

func main() {
	app := iris.New()

	// At the previous example "routing/basic",
	// we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path
	// with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros.

	// iris, like net/http std package registers route's handlers
	// by a Handler, the iris' type of handler is just a func(ctx iris.Context)
	// where context comes from github.com/kataras/iris/context.
	// Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary.
	//
	// iris has the easiest and the most powerful routing process you have ever meet.
	//
	// At the same time,
	// iris has its own interpeter(yes like a programming language)
	// for route's path syntax and their dynamic path parameters parsing and evaluation,
	// We call them "macros" for shortcut.
	// How? It calculates its needs and if not any special regexp needed then it just
	// registers the route with the low-level underline  path syntax,
	// otherwise it pre-compiles the regexp and adds the necessary middleware(s).
	//
	// Standard macro types for parameters:
	//  +------------------------+
	//  | {param:string}         |
	//  +------------------------+
	// string type
	// anything
	//
	//  +------------------------+
	//  | {param:int}            |
	//  +------------------------+
	// int type
	// only numbers (0-9)
	//
	// +------------------------+
	// | {param:long}           |
	// +------------------------+
	// int64 type
	// only numbers (0-9)
	//
	// +------------------------+
	// | {param:boolean}        |
	// +------------------------+
	// bool type
	// only "1" or "t" or "T" or "TRUE" or "true" or "True"
	// or "0" or "f" or "F" or "FALSE" or "false" or "False"
	//
	//  +------------------------+
	//  | {param:alphabetical}   |
	//  +------------------------+
	// alphabetical/letter type
	// letters only (upper or lowercase)
	//
	//  +------------------------+
	//  | {param:file}           |
	//  +------------------------+
	// file type
	// letters (upper or lowercase)
	// numbers (0-9)
	// underscore (_)
	// dash (-)
	// point (.)
	// no spaces ! or other character
	//
	//  +------------------------+
	//  | {param:path}           |
	//  +------------------------+
	// path type
	// anything, should be the last part, more than one path segment,
	// i.e: /path1/path2/path3 , ctx.Params().Get("param") == "/path1/path2/path3"
	//
	// if type is missing then parameter's type is defaulted to string, so
	// {param} == {param:string}.
	//
	// If a function not found on that type then the `string` macro type's functions are being used.
	//
	//
	// Besides the fact that iris provides the basic types and some default "macro funcs"
	// you are able to register your own too!.
	//
	// Register a named path parameter function:
	// app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
	//  [...]
	//  return true/false -> true means valid.
	// })
	//
	// at the func(argument ...) you can have any standard type, it will be validated before the server starts
	// so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool.
	//
	// {param:string equal(iris)} , "iris" will be the argument here:
	// app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
	// 	return func(paramValue string) bool { return argument == paramValue }
	// })

	// you can use the "string" type which is valid for a single path parameter that can be anything.
	app.Get("/username/{name}", func(ctx iris.Context) {
		ctx.Writef("Hello %s", ctx.Params().Get("name"))
	}) // type is missing = {name:string}

	// Let's register our first macro attached to int macro type.
	// "min" = the function
	// "minValue" = the argument of the function
	// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
	// a user requests a path which contains the :int macro type with the min(...) macro parameter function.
	app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
		// do anything before serve here [...]
		// at this case we don't need to do anything
		return func(paramValue string) bool {
			n, err := strconv.Atoi(paramValue)
			if err != nil {
				return false
			}
			return n >= minValue
		}
	})

	// http://localhost:8080/profile/id>=1
	// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
	// macro parameter functions are optional of course.
	app.Get("/profile/{id:int min(1)}", func(ctx iris.Context) {
		// second parameter is the error but it will always nil because we use macros,
		// the validaton already happened.
		id, _ := ctx.Params().GetInt("id")
		ctx.Writef("Hello id: %d", id)
	})

	// to change the error code per route's macro evaluator:
	app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx iris.Context) {
		id, _ := ctx.Params().GetInt("id")
		friendid, _ := ctx.Params().GetInt("friendid")
		ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
	}) // this will throw e 504 error code instead of 404 if all route's macros not passed.

	// http://localhost:8080/game/a-zA-Z/level/0-9
	// remember, alphabetical is lowercase or uppercase letters only.
	app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
		ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
	})

	app.Get("/lowercase/static", func(ctx iris.Context) {
		ctx.Writef("static and dynamic paths are not conflicted anymore!")
	})

	// let's use a trivial custom regexp that validates a single path parameter
	// which its value is only lowercase letters.

	// http://localhost:8080/lowercase/anylowercase
	app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx iris.Context) {
		ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name"))
	})

	// http://localhost:8080/single_file/app.js
	app.Get("/single_file/{myfile:file}", func(ctx iris.Context) {
		ctx.Writef("file type validates if the parameter value has a form of a file name, got: %s", ctx.Params().Get("myfile"))
	})

	// http://localhost:8080/myfiles/any/directory/here/
	// this is the only macro type that accepts any number of path segments.
	app.Get("/myfiles/{directory:path}", func(ctx iris.Context) {
		ctx.Writef("path type accepts any number of path segments, path after /myfiles/ is: %s", ctx.Params().Get("directory"))
	}) // for wildcard path (any number of path segments) without validation you can use:
	// /myfiles/*

	// "{param}"'s performance is exactly the same of ":param"'s.

	// alternatives -> ":param" for single path parameter and "*" for wildcard path parameter.
	// Note these:
	// if  "/mypath/*" then the parameter name is "*".
	// if  "/mypath/{myparam:path}" then the parameter has two names, one is the "*" and the other is the user-defined "myparam".

	// WARNING:
	// A path parameter name should contain only alphabetical letters. Symbols like  '_' and numbers are NOT allowed.
	// Last, do not confuse `ctx.Params()` with `ctx.Values()`.
	// Path parameter's values goes to `ctx.Params()` and context's local storage
	// that can be used to communicate between handlers and middleware(s) goes to
	// `ctx.Values()`.
	app.Run(iris.Addr(":8080"))
}
<commit_msg>Add one more example for custom router macro functions, relative to https://github.com/kataras/iris/issues/918<commit_after>package main

import (
	"regexp"
	"strconv"

	"github.com/kataras/iris"
)

func main() {
	app := iris.New()

	// At the previous example "routing/basic",
	// we've seen static routes, group of routes, subdomains, wildcard subdomains, a small example of parameterized path
	// with a single known paramete and custom http errors, now it's time to see wildcard parameters and macros.

	// iris, like net/http std package registers route's handlers
	// by a Handler, the iris' type of handler is just a func(ctx iris.Context)
	// where context comes from github.com/kataras/iris/context.
	// Until go 1.9 you will have to import that package too, after go 1.9 this will be not be necessary.
	//
	// iris has the easiest and the most powerful routing process you have ever meet.
	//
	// At the same time,
	// iris has its own interpeter(yes like a programming language)
	// for route's path syntax and their dynamic path parameters parsing and evaluation,
	// We call them "macros" for shortcut.
	// How? It calculates its needs and if not any special regexp needed then it just
	// registers the route with the low-level underline  path syntax,
	// otherwise it pre-compiles the regexp and adds the necessary middleware(s).
	//
	// Standard macro types for parameters:
	//  +------------------------+
	//  | {param:string}         |
	//  +------------------------+
	// string type
	// anything
	//
	//  +------------------------+
	//  | {param:int}            |
	//  +------------------------+
	// int type
	// only numbers (0-9)
	//
	// +------------------------+
	// | {param:long}           |
	// +------------------------+
	// int64 type
	// only numbers (0-9)
	//
	// +------------------------+
	// | {param:boolean}        |
	// +------------------------+
	// bool type
	// only "1" or "t" or "T" or "TRUE" or "true" or "True"
	// or "0" or "f" or "F" or "FALSE" or "false" or "False"
	//
	//  +------------------------+
	//  | {param:alphabetical}   |
	//  +------------------------+
	// alphabetical/letter type
	// letters only (upper or lowercase)
	//
	//  +------------------------+
	//  | {param:file}           |
	//  +------------------------+
	// file type
	// letters (upper or lowercase)
	// numbers (0-9)
	// underscore (_)
	// dash (-)
	// point (.)
	// no spaces ! or other character
	//
	//  +------------------------+
	//  | {param:path}           |
	//  +------------------------+
	// path type
	// anything, should be the last part, more than one path segment,
	// i.e: /path1/path2/path3 , ctx.Params().Get("param") == "/path1/path2/path3"
	//
	// if type is missing then parameter's type is defaulted to string, so
	// {param} == {param:string}.
	//
	// If a function not found on that type then the `string` macro type's functions are being used.
	//
	//
	// Besides the fact that iris provides the basic types and some default "macro funcs"
	// you are able to register your own too!.
	//
	// Register a named path parameter function:
	// app.Macros().Int.RegisterFunc("min", func(argument int) func(paramValue string) bool {
	//  [...]
	//  return true/false -> true means valid.
	// })
	//
	// at the func(argument ...) you can have any standard type, it will be validated before the server starts
	// so don't care about performance here, the only thing it runs at serve time is the returning func(paramValue string) bool.
	//
	// {param:string equal(iris)} , "iris" will be the argument here:
	// app.Macros().String.RegisterFunc("equal", func(argument string) func(paramValue string) bool {
	// 	return func(paramValue string) bool { return argument == paramValue }
	// })

	// you can use the "string" type which is valid for a single path parameter that can be anything.
	app.Get("/username/{name}", func(ctx iris.Context) {
		ctx.Writef("Hello %s", ctx.Params().Get("name"))
	}) // type is missing = {name:string}

	// Let's register our first macro attached to int macro type.
	// "min" = the function
	// "minValue" = the argument of the function
	// func(string) bool = the macro's path parameter evaluator, this executes in serve time when
	// a user requests a path which contains the :int macro type with the min(...) macro parameter function.
	app.Macros().Int.RegisterFunc("min", func(minValue int) func(string) bool {
		// do anything before serve here [...]
		// at this case we don't need to do anything
		return func(paramValue string) bool {
			n, err := strconv.Atoi(paramValue)
			if err != nil {
				return false
			}
			return n >= minValue
		}
	})

	// http://localhost:8080/profile/id>=1
	// this will throw 404 even if it's found as route on : /profile/0, /profile/blabla, /profile/-1
	// macro parameter functions are optional of course.
	app.Get("/profile/{id:int min(1)}", func(ctx iris.Context) {
		// second parameter is the error but it will always nil because we use macros,
		// the validaton already happened.
		id, _ := ctx.Params().GetInt("id")
		ctx.Writef("Hello id: %d", id)
	})

	// to change the error code per route's macro evaluator:
	app.Get("/profile/{id:int min(1)}/friends/{friendid:int min(1) else 504}", func(ctx iris.Context) {
		id, _ := ctx.Params().GetInt("id")
		friendid, _ := ctx.Params().GetInt("friendid")
		ctx.Writef("Hello id: %d looking for friend id: ", id, friendid)
	}) // this will throw e 504 error code instead of 404 if all route's macros not passed.

	// Another example using a custom regexp and any custom logic.
	latLonExpr := "^-?[0-9]{1,3}(?:\\.[0-9]{1,10})?$"
	latLonRegex, err := regexp.Compile(latLonExpr)
	if err != nil {
		panic(err)
	}

	app.Macros().String.RegisterFunc("coordinate", func() func(paramName string) (ok bool) {
		// MatchString is a type of func(string) bool, so we can return that as it's.
		return latLonRegex.MatchString
	})

	app.Get("/coordinates/{lat:string coordinate() else 502}/{lon:string coordinate() else 502}", func(ctx iris.Context) {
		ctx.Writef("Lat: %s | Lon: %s", ctx.Params().Get("lat"), ctx.Params().Get("lon"))
	})

	//

	// http://localhost:8080/game/a-zA-Z/level/0-9
	// remember, alphabetical is lowercase or uppercase letters only.
	app.Get("/game/{name:alphabetical}/level/{level:int}", func(ctx iris.Context) {
		ctx.Writef("name: %s | level: %s", ctx.Params().Get("name"), ctx.Params().Get("level"))
	})

	app.Get("/lowercase/static", func(ctx iris.Context) {
		ctx.Writef("static and dynamic paths are not conflicted anymore!")
	})

	// let's use a trivial custom regexp that validates a single path parameter
	// which its value is only lowercase letters.

	// http://localhost:8080/lowercase/anylowercase
	app.Get("/lowercase/{name:string regexp(^[a-z]+)}", func(ctx iris.Context) {
		ctx.Writef("name should be only lowercase, otherwise this handler will never executed: %s", ctx.Params().Get("name"))
	})

	// http://localhost:8080/single_file/app.js
	app.Get("/single_file/{myfile:file}", func(ctx iris.Context) {
		ctx.Writef("file type validates if the parameter value has a form of a file name, got: %s", ctx.Params().Get("myfile"))
	})

	// http://localhost:8080/myfiles/any/directory/here/
	// this is the only macro type that accepts any number of path segments.
	app.Get("/myfiles/{directory:path}", func(ctx iris.Context) {
		ctx.Writef("path type accepts any number of path segments, path after /myfiles/ is: %s", ctx.Params().Get("directory"))
	}) // for wildcard path (any number of path segments) without validation you can use:
	// /myfiles/*

	// "{param}"'s performance is exactly the same of ":param"'s.

	// alternatives -> ":param" for single path parameter and "*" for wildcard path parameter.
	// Note these:
	// if  "/mypath/*" then the parameter name is "*".
	// if  "/mypath/{myparam:path}" then the parameter has two names, one is the "*" and the other is the user-defined "myparam".

	// WARNING:
	// A path parameter name should contain only alphabetical letters. Symbols like  '_' and numbers are NOT allowed.
	// Last, do not confuse `ctx.Params()` with `ctx.Values()`.
	// Path parameter's values goes to `ctx.Params()` and context's local storage
	// that can be used to communicate between handlers and middleware(s) goes to
	// `ctx.Values()`.
	app.Run(iris.Addr(":8080"))
}
<|endoftext|>


<commit_before>package core

import (
	"errors"
	"io/ioutil"
	"os/exec"
	"testing"
	"time"

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

// MockIntegration is an autogenerated mock type for the MockIntegration type
type MockIntegration struct {
	mock.Mock
}

// AttachToApp provides a mock function with given fields: _a0
func (_m *MockIntegration) AttachToApp(_a0 App) error {
	ret := _m.Called(_a0)

	var r0 error
	if rf, ok := ret.Get(0).(func(App) error); ok {
		r0 = rf(_a0)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Identifier provides a mock function with given fields:
func (_m *MockIntegration) Identifier() string {
	ret := _m.Called()

	var r0 string
	if rf, ok := ret.Get(0).(func() string); ok {
		r0 = rf()
	} else {
		r0 = ret.Get(0).(string)
	}

	return r0
}

// IsProvider provides a mock function with given fields: _a0
func (_m *MockIntegration) IsProvider(_a0 string) bool {
	ret := _m.Called(_a0)

	var r0 bool
	if rf, ok := ret.Get(0).(func(string) bool); ok {
		r0 = rf(_a0)
	} else {
		r0 = ret.Get(0).(bool)
	}

	return r0
}

// ProvideFor provides a mock function with given fields: c, directory
func (_m *MockIntegration) ProvideFor(c *BuildConfig, directory string) error {
	ret := _m.Called(c, directory)

	var r0 error
	if rf, ok := ret.Get(0).(func(*BuildConfig, string) error); ok {
		r0 = rf(c, directory)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Shutdown provides a mock function with given fields:
func (_m *MockIntegration) Shutdown() {
	_m.Called()
}

// mockApp is an autogenerated mock type for the mockApp type
type mockApp struct {
	mock.Mock
}

// AppLocation provides a mock function with given fields:
func (_m *mockApp) AppLocation() string {
	ret := _m.Called()

	var r0 string
	if rf, ok := ret.Get(0).(func() string); ok {
		r0 = rf()
	} else {
		r0 = ret.Get(0).(string)
	}

	return r0
}

// Config provides a mock function with given fields: namespace, conf
func (_m *mockApp) Config(namespace string, conf interface{}) error {
	ret := _m.Called(namespace, conf)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, interface{}) error); ok {
		r0 = rf(namespace, conf)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// GetBuild provides a mock function with given fields: token
func (_m *mockApp) GetBuild(token string) (Build, error) {
	ret := _m.Called(token)

	var r0 Build
	if rf, ok := ret.Get(0).(func(string) Build); ok {
		r0 = rf(token)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(Build)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(token)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetBuildHistory provides a mock function with given fields: group
func (_m *mockApp) GetBuildHistory(group string) []Build {
	ret := _m.Called(group)

	var r0 []Build
	if rf, ok := ret.Get(0).(func(string) []Build); ok {
		r0 = rf(group)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).([]Build)
		}
	}

	return r0
}

// GlobalConfig provides a mock function with given fields: conf
func (_m *mockApp) GlobalConfig(conf interface{}) error {
	ret := _m.Called(conf)

	var r0 error
	if rf, ok := ret.Get(0).(func(interface{}) error); ok {
		r0 = rf(conf)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Listen provides a mock function with given fields: event, listener
func (_m *mockApp) Listen(event string, listener func(map[string]string)) EventHandler {
	ret := _m.Called(event, listener)

	var r0 EventHandler
	if rf, ok := ret.Get(0).(func(string, func(map[string]string)) EventHandler); ok {
		r0 = rf(event, listener)
	} else {
		r0 = ret.Get(0).(EventHandler)
	}

	return r0
}

// Logcritf provides a mock function with given fields: _a0, _a1
func (_m *mockApp) Logcritf(_a0 string, _a1 ...interface{}) {
	_m.Called(_a0, _a1)
}

// Loginfof provides a mock function with given fields: _a0, _a1
func (_m *mockApp) Loginfof(_a0 string, _a1 ...interface{}) {
	_m.Called(_a0, _a1)
}

// Logwarnf provides a mock function with given fields: _a0, _a1
func (_m *mockApp) Logwarnf(_a0 string, _a1 ...interface{}) {
	_m.Called(_a0, _a1)
}

// Name provides a mock function with given fields:
func (_m *mockApp) Name() string {
	ret := _m.Called()

	var r0 string
	if rf, ok := ret.Get(0).(func() string); ok {
		r0 = rf()
	} else {
		r0 = ret.Get(0).(string)
	}

	return r0
}

// NewBuild provides a mock function with given fields: group, config
func (_m *mockApp) NewBuild(group string, config *BuildConfig) (string, error) {
	ret := _m.Called(group, config)

	var r0 string
	if rf, ok := ret.Get(0).(func(string, *BuildConfig) string); ok {
		r0 = rf(group, config)
	} else {
		r0 = ret.Get(0).(string)
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, *BuildConfig) error); ok {
		r1 = rf(group, config)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// RemoveEventHandler provides a mock function with given fields: _a0
func (_m *mockApp) RemoveEventHandler(_a0 EventHandler) {
	_m.Called(_a0)
}

// SendEvent provides a mock function with given fields: event
func (_m *mockApp) SendEvent(event string) {
	_m.Called(event)
}

// Shutdown provides a mock function with given fields:
func (_m *mockApp) Shutdown() {
	_m.Called()
}

func getSuccessfulIntegration() *MockIntegration {
	i := &MockIntegration{}
	i.On("Identifier").Return("Success")
	i.On("IsProvider", mock.Anything).Return(true)
	i.On("ProvideFor", mock.AnythingOfType("*core.BuildConfig"), mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
		dir := args.Get(1).(string)
		//FIXME - this is lazy, stops tests running on windows, is bad in general, i'm so tired
		cmd := exec.Command("cp", "testdata/failure.sh", "testdata/success.sh", "testdata/fiveminutes.sh", dir)
		cmd.Run()
	}).Return(nil)
	return i
}

func getFailedIntegration() *MockIntegration {
	i := &MockIntegration{}
	i.On("Identifier").Return("Success")
	i.On("IsProvider", mock.Anything).Return(true)
	i.On("ProvideFor", mock.Anything, mock.Anything).Return(errors.New("testmarker"))
	return i
}

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

	app := &mockApp{}
	app.On("SendEvent", mock.AnythingOfType("string")).Return()
	app.On("Name").Return("MockApp")
	app.On("Logcritf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Logwarnf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Loginfof", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()

	b := build{token: "testtoken", parentApp: app}
	dir, err := provisionDirectory()
	require.NoError(err)

	integrationSuccess := getSuccessfulIntegration()
	integrationFailure := getFailedIntegration()

	config := BuildConfig{
		BaseRepo:     "testmarker-baserepo",
		HeadRepo:     "testmarker-mergerepo",
		Integrations: []Integration{integrationFailure, integrationSuccess},
	}

	assert.NoError(b.provisionBuildIntoDirectory(&config, dir))
	assert.NoError(cleanupDirectory(dir))
}

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

	app := &mockApp{}
	app.On("SendEvent", mock.AnythingOfType("string")).Return()
	app.On("Name").Return("MockApp")
	app.On("Logcritf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Logwarnf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Loginfof", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()

	b := build{token: "testtoken", parentApp: app}
	b.config = &BuildConfig{
		Integrations: []Integration{getSuccessfulIntegration()},
		BuildRunner:  "success.sh",
		Deadline:     time.Second * 5,
	}
	b.Ref()

	require.NoError(b.runBuildSync(*b.config))

	stdoutpipe, err := b.Stdout()
	require.NoError(err)
	stdout, err := ioutil.ReadAll(stdoutpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stdout)

	stderrpipe, err := b.Stderr()
	require.NoError(err)
	stderr, err := ioutil.ReadAll(stderrpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stderr)
	b.Unref()
	assert.Empty(b.buildDirectory)

	require.True(b.state.HasStopped())
}

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

	app := &mockApp{}
	app.On("SendEvent", mock.AnythingOfType("string")).Return()
	app.On("Name").Return("MockApp")
	app.On("Logcritf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Logwarnf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Loginfof", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()

	b := build{token: "testtoken", parentApp: app}
	b.config = &BuildConfig{
		Integrations: []Integration{getSuccessfulIntegration()},
		BuildRunner:  "failure.sh",
		Deadline:     time.Second * 5,
	}
	b.Ref()

	require.Error(b.runBuildSync(*b.config))

	stdoutpipe, err := b.Stdout()
	require.NoError(err)
	stdout, err := ioutil.ReadAll(stdoutpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stdout)

	stderrpipe, err := b.Stderr()
	require.NoError(err)
	stderr, err := ioutil.ReadAll(stderrpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stderr)
	b.Unref()
	assert.Empty(b.buildDirectory)
	require.True(b.state.HasStopped())
}

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

	app := &mockApp{}
	app.On("SendEvent", mock.AnythingOfType("string")).Return()
	app.On("Name").Return("MockApp")
	app.On("Logcritf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Logwarnf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Loginfof", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()

	b := build{token: "testtoken", parentApp: app}
	b.config = &BuildConfig{
		Integrations: []Integration{getSuccessfulIntegration()},
		BuildRunner:  "fiveminutes.sh",
		Deadline:     time.Second,
	}
	b.Ref()

	require.Error(b.runBuildSync(*b.config))

	stdoutpipe, err := b.Stdout()
	require.NoError(err)
	stdout, err := ioutil.ReadAll(stdoutpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stdout)
	b.Unref()
	assert.Empty(b.buildDirectory)
	require.True(b.state.HasStopped())
}
<commit_msg>fix tests<commit_after>package core

import (
	"errors"
	"io/ioutil"
	"os/exec"
	"testing"
	"time"

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

// MockIntegration is an autogenerated mock type for the MockIntegration type
type MockIntegration struct {
	mock.Mock
}

// AttachToApp provides a mock function with given fields: _a0
func (_m *MockIntegration) AttachToApp(_a0 App) error {
	ret := _m.Called(_a0)

	var r0 error
	if rf, ok := ret.Get(0).(func(App) error); ok {
		r0 = rf(_a0)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Identifier provides a mock function with given fields:
func (_m *MockIntegration) Identifier() string {
	ret := _m.Called()

	var r0 string
	if rf, ok := ret.Get(0).(func() string); ok {
		r0 = rf()
	} else {
		r0 = ret.Get(0).(string)
	}

	return r0
}

// IsProvider provides a mock function with given fields: _a0
func (_m *MockIntegration) IsProvider(_a0 string) bool {
	ret := _m.Called(_a0)

	var r0 bool
	if rf, ok := ret.Get(0).(func(string) bool); ok {
		r0 = rf(_a0)
	} else {
		r0 = ret.Get(0).(bool)
	}

	return r0
}

// ProvideFor provides a mock function with given fields: c, directory
func (_m *MockIntegration) ProvideFor(c *BuildConfig, directory string) error {
	ret := _m.Called(c, directory)

	var r0 error
	if rf, ok := ret.Get(0).(func(*BuildConfig, string) error); ok {
		r0 = rf(c, directory)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Shutdown provides a mock function with given fields:
func (_m *MockIntegration) Shutdown() {
	_m.Called()
}

// mockApp is an autogenerated mock type for the mockApp type
type mockApp struct {
	mock.Mock
}

// AppLocation provides a mock function with given fields:
func (_m *mockApp) AppLocation() string {
	ret := _m.Called()

	var r0 string
	if rf, ok := ret.Get(0).(func() string); ok {
		r0 = rf()
	} else {
		r0 = ret.Get(0).(string)
	}

	return r0
}

// Config provides a mock function with given fields: namespace, conf
func (_m *mockApp) Config(namespace string, conf interface{}) error {
	ret := _m.Called(namespace, conf)

	var r0 error
	if rf, ok := ret.Get(0).(func(string, interface{}) error); ok {
		r0 = rf(namespace, conf)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// GetBuild provides a mock function with given fields: token
func (_m *mockApp) GetBuild(token string) (Build, error) {
	ret := _m.Called(token)

	var r0 Build
	if rf, ok := ret.Get(0).(func(string) Build); ok {
		r0 = rf(token)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).(Build)
		}
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string) error); ok {
		r1 = rf(token)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// GetBuildHistory provides a mock function with given fields: group
func (_m *mockApp) GetBuildHistory(group string) []Build {
	ret := _m.Called(group)

	var r0 []Build
	if rf, ok := ret.Get(0).(func(string) []Build); ok {
		r0 = rf(group)
	} else {
		if ret.Get(0) != nil {
			r0 = ret.Get(0).([]Build)
		}
	}

	return r0
}

// GlobalConfig provides a mock function with given fields: conf
func (_m *mockApp) GlobalConfig(conf interface{}) error {
	ret := _m.Called(conf)

	var r0 error
	if rf, ok := ret.Get(0).(func(interface{}) error); ok {
		r0 = rf(conf)
	} else {
		r0 = ret.Error(0)
	}

	return r0
}

// Listen provides a mock function with given fields: event, listener
func (_m *mockApp) Listen(event string, listener func(map[string]string)) EventHandler {
	ret := _m.Called(event, listener)

	var r0 EventHandler
	if rf, ok := ret.Get(0).(func(string, func(map[string]string)) EventHandler); ok {
		r0 = rf(event, listener)
	} else {
		r0 = ret.Get(0).(EventHandler)
	}

	return r0
}

// Logcritf provides a mock function with given fields: _a0, _a1
func (_m *mockApp) Logcritf(_a0 string, _a1 ...interface{}) {
	_m.Called(_a0, _a1)
}

// Loginfof provides a mock function with given fields: _a0, _a1
func (_m *mockApp) Loginfof(_a0 string, _a1 ...interface{}) {
	_m.Called(_a0, _a1)
}

// Logwarnf provides a mock function with given fields: _a0, _a1
func (_m *mockApp) Logwarnf(_a0 string, _a1 ...interface{}) {
	_m.Called(_a0, _a1)
}

// Name provides a mock function with given fields:
func (_m *mockApp) Name() string {
	ret := _m.Called()

	var r0 string
	if rf, ok := ret.Get(0).(func() string); ok {
		r0 = rf()
	} else {
		r0 = ret.Get(0).(string)
	}

	return r0
}

// NewBuild provides a mock function with given fields: group, config
func (_m *mockApp) NewBuild(group string, config *BuildConfig) (string, error) {
	ret := _m.Called(group, config)

	var r0 string
	if rf, ok := ret.Get(0).(func(string, *BuildConfig) string); ok {
		r0 = rf(group, config)
	} else {
		r0 = ret.Get(0).(string)
	}

	var r1 error
	if rf, ok := ret.Get(1).(func(string, *BuildConfig) error); ok {
		r1 = rf(group, config)
	} else {
		r1 = ret.Error(1)
	}

	return r0, r1
}

// RemoveEventHandler provides a mock function with given fields: _a0
func (_m *mockApp) RemoveEventHandler(_a0 EventHandler) {
	_m.Called(_a0)
}

// SendEvent provides a mock function with given fields: event
func (_m *mockApp) SendEvent(event string) {
	_m.Called(event)
}

// Shutdown provides a mock function with given fields:
func (_m *mockApp) Shutdown() {
	_m.Called()
}

func getSuccessfulIntegration() *MockIntegration {
	i := &MockIntegration{}
	i.On("Identifier").Return("Success")
	i.On("IsProvider", mock.Anything).Return(true)
	i.On("ProvideFor", mock.AnythingOfType("*core.BuildConfig"), mock.AnythingOfType("string")).Run(func(args mock.Arguments) {
		dir := args.Get(1).(string)
		//FIXME - this is lazy, stops tests running on windows, is bad in general, i'm so tired
		cmd := exec.Command("cp", "testdata/failure.sh", "testdata/success.sh", "testdata/fiveminutes.sh", dir)
		cmd.Run()
	}).Return(nil)
	return i
}

func getFailedIntegration() *MockIntegration {
	i := &MockIntegration{}
	i.On("Identifier").Return("Success")
	i.On("IsProvider", mock.Anything).Return(true)
	i.On("ProvideFor", mock.Anything, mock.Anything).Return(errors.New("testmarker"))
	return i
}

func getMockApp() App {
	app := &mockApp{}
	app.On("SendEvent", mock.AnythingOfType("string")).Return()
	app.On("Name").Return("MockApp")
	app.On("Logcritf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Logwarnf", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("Loginfof", mock.AnythingOfType("string"), mock.AnythingOfType("[]interface {}")).Return()
	app.On("GlobalConfig", mock.Anything).Return(nil)

	return app
}

func TestProvisionBuildIntoDirectory(t *testing.T) {
	assert := assert.New(t)
	require := require.New(t)
	app := getMockApp()
	b := build{token: "testtoken", parentApp: app}
	dir, err := provisionDirectory("")
	require.NoError(err)

	integrationSuccess := getSuccessfulIntegration()
	integrationFailure := getFailedIntegration()

	config := BuildConfig{
		BaseRepo:     "testmarker-baserepo",
		HeadRepo:     "testmarker-mergerepo",
		Integrations: []Integration{integrationFailure, integrationSuccess},
	}

	assert.NoError(b.provisionBuildIntoDirectory(&config, dir))
	assert.NoError(cleanupDirectory(dir))
}

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

	app := getMockApp()
	b := build{token: "testtoken", parentApp: app}
	b.config = &BuildConfig{
		Integrations: []Integration{getSuccessfulIntegration()},
		BuildRunner:  "success.sh",
		Deadline:     time.Second * 5,
	}
	b.Ref()

	require.NoError(b.runBuildSync(*b.config))

	stdoutpipe, err := b.Stdout()
	require.NoError(err)
	stdout, err := ioutil.ReadAll(stdoutpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stdout)

	stderrpipe, err := b.Stderr()
	require.NoError(err)
	stderr, err := ioutil.ReadAll(stderrpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stderr)
	b.Unref()
	assert.Empty(b.buildDirectory)

	require.True(b.state.HasStopped())
}

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

	app := getMockApp()
	b := build{token: "testtoken", parentApp: app}
	b.config = &BuildConfig{
		Integrations: []Integration{getSuccessfulIntegration()},
		BuildRunner:  "failure.sh",
		Deadline:     time.Second * 5,
	}
	b.Ref()

	require.Error(b.runBuildSync(*b.config))

	stdoutpipe, err := b.Stdout()
	require.NoError(err)
	stdout, err := ioutil.ReadAll(stdoutpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stdout)

	stderrpipe, err := b.Stderr()
	require.NoError(err)
	stderr, err := ioutil.ReadAll(stderrpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stderr)
	b.Unref()
	assert.Empty(b.buildDirectory)
	require.True(b.state.HasStopped())
}

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

	app := getMockApp()
	b := build{token: "testtoken", parentApp: app}
	b.config = &BuildConfig{
		Integrations: []Integration{getSuccessfulIntegration()},
		BuildRunner:  "fiveminutes.sh",
		Deadline:     time.Second,
	}
	b.Ref()

	require.Error(b.runBuildSync(*b.config))

	stdoutpipe, err := b.Stdout()
	require.NoError(err)
	stdout, err := ioutil.ReadAll(stdoutpipe)
	require.NoError(err)
	assert.EqualValues([]byte("testmarker\n"), stdout)
	b.Unref()
	assert.Empty(b.buildDirectory)
	require.True(b.state.HasStopped())
}
<|endoftext|>


<commit_before>package configuration

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

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/viper"
	"gopkg.in/yaml.v2"
)

type Flags []string

type Config struct {
	DefaultTarget string            `yaml:"default"`
	Targets       map[string]Target `yaml:"targets"`
}

func GetConfig() *Config {
	err := viper.ReadInConfig()
	if err != nil {
		log.Debug(err.Error())
	}

	return &Config{
		DefaultTarget: viper.GetString("default"),
		Targets:       getTargetsFromConfig(viper.GetStringMap("targets")),
	}
}

func (this *Config) GetTarget(targetName string) *Target {
	if targetName == "" {
		targetName = this.DefaultTarget
	}

	for key, target := range this.Targets {
		if key == targetName {
			return &target
		}
	}

	return nil
}

func (this *Config) NewTarget(target Target) {
	this.Targets[target.Name] = target
}

func (this *Config) DeleteTarget(targetToDelete Target) {
	targets := map[string]Target{}

	for key, target := range this.Targets {
		if key != targetToDelete.Name {
			targets[key] = target
		}
	}

	this.Targets = targets
}

func (c *Config) GetFilepath() string {
	return viper.ConfigFileUsed()
}

func (c *Config) WriteToFile(hoverflyDirectory HoverflyDirectory) error {
	data, err := yaml.Marshal(c)

	if err != nil {
		log.Debug(err.Error())
		return err
	}

	filepath := filepath.Join(hoverflyDirectory.Path, "config.yaml")

	err = ioutil.WriteFile(filepath, data, 0644)

	if err != nil {
		log.Debug(err.Error())
		return err
	}

	return nil
}

func SetConfigurationPaths() {
	viper.AddConfigPath("./.hoverfly")
	viper.AddConfigPath("$HOME/.hoverfly")
}

func SetConfigurationDefaults() {
	viper.SetDefault("default", "local")
}
<commit_msg>Add better error when viper fails first time in hoverctl<commit_after>package configuration

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

	log "github.com/Sirupsen/logrus"
	"github.com/spf13/viper"
	"gopkg.in/yaml.v2"
)

type Flags []string

type Config struct {
	DefaultTarget string            `yaml:"default"`
	Targets       map[string]Target `yaml:"targets"`
}

func GetConfig() *Config {
	err := viper.ReadInConfig()
	if err != nil {
		log.Debug("Error reading config")
		if err.Error() == `Unsupported Config Type ""` {
			log.Debug("viper not properly configured, if this is the first time executing hoverctl, please try te command again")
		} else {
			log.Debug(err.Error())
		}
	}

	return &Config{
		DefaultTarget: viper.GetString("default"),
		Targets:       getTargetsFromConfig(viper.GetStringMap("targets")),
	}
}

func (this *Config) GetTarget(targetName string) *Target {
	if targetName == "" {
		targetName = this.DefaultTarget
	}

	for key, target := range this.Targets {
		if key == targetName {
			return &target
		}
	}

	return nil
}

func (this *Config) NewTarget(target Target) {
	this.Targets[target.Name] = target
}

func (this *Config) DeleteTarget(targetToDelete Target) {
	targets := map[string]Target{}

	for key, target := range this.Targets {
		if key != targetToDelete.Name {
			targets[key] = target
		}
	}

	this.Targets = targets
}

func (c *Config) GetFilepath() string {
	return viper.ConfigFileUsed()
}

func (c *Config) WriteToFile(hoverflyDirectory HoverflyDirectory) error {
	data, err := yaml.Marshal(c)

	if err != nil {
		log.Debug(err.Error())
		return err
	}

	filepath := filepath.Join(hoverflyDirectory.Path, "config.yaml")

	err = ioutil.WriteFile(filepath, data, 0644)

	if err != nil {
		log.Debug(err.Error())
		return err
	}

	return nil
}

func SetConfigurationPaths() {
	viper.AddConfigPath("./.hoverfly")
	viper.AddConfigPath("$HOME/.hoverfly")
}

func SetConfigurationDefaults() {
	viper.SetDefault("default", "local")
}
<|endoftext|>


<commit_before>// Copyright 2016 Hajime Hoshi
//
// 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 graphics

import (
	"image"
	"image/color"

	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

type Image struct {
	texture     *texture
	framebuffer *framebuffer
	width       int
	height      int
}

func NewImage(width, height int, filter opengl.Filter) (*Image, error) {
	i := &Image{
		width:  width,
		height: height,
	}
	c := &newImageCommand{
		result: i,
		width:  width,
		height: height,
		filter: filter,
	}
	theCommandQueue.Enqueue(c)
	return i, nil
}

func NewImageFromImage(img *image.RGBA, filter opengl.Filter) (*Image, error) {
	s := img.Bounds().Size()
	i := &Image{
		width:  s.X,
		height: s.Y,
	}
	c := &newImageFromImageCommand{
		result: i,
		img:    img,
		filter: filter,
	}
	theCommandQueue.Enqueue(c)
	return i, nil
}

func NewScreenFramebufferImage(width, height int) (*Image, error) {
	i := &Image{
		width:  width,
		height: height,
	}
	c := &newScreenFramebufferImageCommand{
		result: i,
		width:  width,
		height: height,
	}
	theCommandQueue.Enqueue(c)
	return i, nil
}

func (i *Image) Dispose() error {
	c := &disposeCommand{
		target: i,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) Size() (int, int) {
	return i.width, i.height
}

func (i *Image) Fill(clr color.Color) error {
	c := &fillCommand{
		dst:   i,
		color: clr,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) DrawImage(src *Image, vertices []int16, geo, clr Matrix, mode opengl.CompositeMode) error {
	c := &drawImageCommand{
		dst:      i,
		src:      src,
		vertices: vertices,
		geo:      geo,
		color:    clr,
		mode:     mode,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) Pixels(context *opengl.Context) ([]uint8, error) {
	// Flush the enqueued commands so that pixels are certainly read.
	if err := theCommandQueue.Flush(context); err != nil {
		return nil, err
	}
	f := i.framebuffer
	return context.FramebufferPixels(f.native, i.width, i.height)
}

func (i *Image) ReplacePixels(p []uint8) error {
	c := &replacePixelsCommand{
		dst:    i,
		pixels: p,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) IsInvalidated(context *opengl.Context) bool {
	return !context.IsTexture(i.texture.native)
}
<commit_msg>graphics: Bug fix: Pixels should be copied to store it as a command<commit_after>// Copyright 2016 Hajime Hoshi
//
// 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 graphics

import (
	"image"
	"image/color"

	"github.com/hajimehoshi/ebiten/internal/graphics/opengl"
)

type Image struct {
	texture     *texture
	framebuffer *framebuffer
	width       int
	height      int
}

func NewImage(width, height int, filter opengl.Filter) (*Image, error) {
	i := &Image{
		width:  width,
		height: height,
	}
	c := &newImageCommand{
		result: i,
		width:  width,
		height: height,
		filter: filter,
	}
	theCommandQueue.Enqueue(c)
	return i, nil
}

func NewImageFromImage(img *image.RGBA, filter opengl.Filter) (*Image, error) {
	s := img.Bounds().Size()
	i := &Image{
		width:  s.X,
		height: s.Y,
	}
	c := &newImageFromImageCommand{
		result: i,
		img:    img,
		filter: filter,
	}
	theCommandQueue.Enqueue(c)
	return i, nil
}

func NewScreenFramebufferImage(width, height int) (*Image, error) {
	i := &Image{
		width:  width,
		height: height,
	}
	c := &newScreenFramebufferImageCommand{
		result: i,
		width:  width,
		height: height,
	}
	theCommandQueue.Enqueue(c)
	return i, nil
}

func (i *Image) Dispose() error {
	c := &disposeCommand{
		target: i,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) Size() (int, int) {
	return i.width, i.height
}

func (i *Image) Fill(clr color.Color) error {
	c := &fillCommand{
		dst:   i,
		color: clr,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) DrawImage(src *Image, vertices []int16, geo, clr Matrix, mode opengl.CompositeMode) error {
	c := &drawImageCommand{
		dst:      i,
		src:      src,
		vertices: vertices,
		geo:      geo,
		color:    clr,
		mode:     mode,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) Pixels(context *opengl.Context) ([]uint8, error) {
	// Flush the enqueued commands so that pixels are certainly read.
	if err := theCommandQueue.Flush(context); err != nil {
		return nil, err
	}
	f := i.framebuffer
	return context.FramebufferPixels(f.native, i.width, i.height)
}

func (i *Image) ReplacePixels(p []uint8) error {
	pixels := make([]uint8, len(p))
	copy(pixels, p)
	c := &replacePixelsCommand{
		dst:    i,
		pixels: pixels,
	}
	theCommandQueue.Enqueue(c)
	return nil
}

func (i *Image) IsInvalidated(context *opengl.Context) bool {
	return !context.IsTexture(i.texture.native)
}
<|endoftext|>


<commit_before>package bouncermain

import ()

type DispatcherFunc func(*Request, *Reply) error

var dispatcherTable = map[string]DispatcherFunc{
	"tokenbucket.get":   TokenBucketGet,
	"semaphore.acquire": SemaphoreAcquire,
	"semaphore.release": SemaphoreRelease,
	"event.wait":        EventWait,
	"event.send":        EventSend,
	"watchdog.kick":     WatchdogKick,
}

func dispatchRequest(req *Request, rep *Reply) error {
	method, ok := dispatcherTable[req.Method]
	if !ok {
		return ErrInvalidMethod
	}

	return method(req, rep)
}

func TokenBucketGet(req *Request, rep *Reply) error {

	if req.Size == 0 {
		return ErrSizeRequired
	}

	if req.MaxWait < 0 {
		return ErrInvalidMaxWait
	}

	if req.Interval == 0 {
		req.Interval = 1000
	}

	if req.Interval < 0 {
		return ErrInvalidInterval
	}

	bucket := getTokenBucket(req.Name, req.Size, req.Interval)

	_, err := bucket.Acquire(req.MaxWait)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204
	return nil
}

func TokenBucketStats(req *Request, rep *Reply) error {
	// if req.Name == "" {
	// 	return ErrNameRequired
	// }

	// // we don't use getTokenBucket here to avoid creating a new bucket
	// bucket, ok := buckets[req.Name]
	// if !ok {
	// 	return ErrNotFound
	// }

	return nil
}

func SemaphoreAcquire(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.Size == 0 {
		return ErrSizeRequired
	}

	if req.MaxWait < 0 {
		return ErrInvalidMaxWait
	}

	semaphore := getSemaphore(req.Name, req.Size)

	token, err := semaphore.Acquire(req.MaxWait, req.Expire, req.Key)
	if err != nil {
		return err
	}

	rep.Body = token
	rep.Status = 200

	return nil
}

func SemaphoreRelease(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.Key == "" {
		return ErrKeyRequired
	}

	semaphore := getSemaphore(req.Name, req.Size)

	token, err := semaphore.Release(req.Key)
	if err != nil {
		return err
	}

	rep.Body = token
	return nil
}

func EventWait(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.MaxWait < 0 {
		return ErrInvalidMaxWait
	}

	event := getEvent(req.Name)

	message, err := event.Wait(req.MaxWait)
	if err != nil {
		return err
	}

	rep.Body = message
	rep.Status = 200

	return nil
}

func EventSend(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	event := getEvent(req.Name)

	err := event.Send(req.Message)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204

	return nil
}

func WatchdogKick(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.Interval < 1 {
		return ErrInvalidInterval
	}

	watchdog := getWatchdog(req.Name, req.Interval)

	err := watchdog.Kick(req.Interval)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204

	return nil
}
<commit_msg>Modify semaphore.release to return 204 No Content<commit_after>package bouncermain

import ()

type DispatcherFunc func(*Request, *Reply) error

var dispatcherTable = map[string]DispatcherFunc{
	"tokenbucket.get":   TokenBucketGet,
	"semaphore.acquire": SemaphoreAcquire,
	"semaphore.release": SemaphoreRelease,
	"event.wait":        EventWait,
	"event.send":        EventSend,
	"watchdog.kick":     WatchdogKick,
}

func dispatchRequest(req *Request, rep *Reply) error {
	method, ok := dispatcherTable[req.Method]
	if !ok {
		return ErrInvalidMethod
	}

	return method(req, rep)
}

func TokenBucketGet(req *Request, rep *Reply) error {

	if req.Size == 0 {
		return ErrSizeRequired
	}

	if req.MaxWait < 0 {
		return ErrInvalidMaxWait
	}

	if req.Interval == 0 {
		req.Interval = 1000
	}

	if req.Interval < 0 {
		return ErrInvalidInterval
	}

	bucket := getTokenBucket(req.Name, req.Size, req.Interval)

	_, err := bucket.Acquire(req.MaxWait)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204
	return nil
}

func TokenBucketStats(req *Request, rep *Reply) error {
	// if req.Name == "" {
	// 	return ErrNameRequired
	// }

	// // we don't use getTokenBucket here to avoid creating a new bucket
	// bucket, ok := buckets[req.Name]
	// if !ok {
	// 	return ErrNotFound
	// }

	return nil
}

func SemaphoreAcquire(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.Size == 0 {
		return ErrSizeRequired
	}

	if req.MaxWait < 0 {
		return ErrInvalidMaxWait
	}

	semaphore := getSemaphore(req.Name, req.Size)

	token, err := semaphore.Acquire(req.MaxWait, req.Expire, req.Key)
	if err != nil {
		return err
	}

	rep.Body = token
	rep.Status = 200

	return nil
}

func SemaphoreRelease(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.Key == "" {
		return ErrKeyRequired
	}

	semaphore := getSemaphore(req.Name, req.Size)

	_, err := semaphore.Release(req.Key)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204
	return nil
}

func EventWait(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.MaxWait < 0 {
		return ErrInvalidMaxWait
	}

	event := getEvent(req.Name)

	message, err := event.Wait(req.MaxWait)
	if err != nil {
		return err
	}

	rep.Body = message
	rep.Status = 200

	return nil
}

func EventSend(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	event := getEvent(req.Name)

	err := event.Send(req.Message)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204

	return nil
}

func WatchdogKick(req *Request, rep *Reply) error {
	if req.Name == "" {
		return ErrNameRequired
	}

	if req.Interval < 1 {
		return ErrInvalidInterval
	}

	watchdog := getWatchdog(req.Name, req.Interval)

	err := watchdog.Kick(req.Interval)
	if err != nil {
		return err
	}

	rep.Body = ""
	rep.Status = 204

	return nil
}
<|endoftext|>


<commit_before>// +build darwin dragonfly freebsd netbsd openbsd

// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package bsdbpf

import (
	"github.com/google/gopacket"
	"golang.org/x/sys/unix"

	"fmt"
	"syscall"
	"time"
	"unsafe"
)

const wordSize = int(unsafe.Sizeof(uintptr(0)))

func bpfWordAlign(x int) int {
	return (((x) + (wordSize - 1)) &^ (wordSize - 1))
}

type TimedFrame struct {
	RawFrame  []byte
	Timestamp time.Time
}

type BPFSniffer struct {
	sniffDeviceName string
	bpfDeviceName string
	fd       int
	stopChan chan bool
	readChan chan TimedFrame
	readBuffer []byte
	readBufLen int
	lastReadLen int
	readBytesConsumed int
}

func NewBPFSniffer(sniffDeviceName, bpfDeviceName string, readBufLen int) *BPFSniffer {
	return &BPFSniffer{
		sniffDeviceName: sniffDeviceName,
		bpfDeviceName: bpfDeviceName,
		stopChan: make(chan bool, 0),
		readChan: make(chan TimedFrame, 0),
		readBufLen: readBufLen,
	}
}

func (b *BPFSniffer) Close() error {
	return syscall.Close(b.fd)
}

func (b *BPFSniffer) pickBpfDevice() {
	var err error
	for i := 0; i < 99; i++ {
		b.bpfDeviceName = fmt.Sprintf("/dev/bpf%d", i)
		b.fd, err = syscall.Open(b.bpfDeviceName, syscall.O_RDWR, 0)
		if err == nil {
			break
		}
	}
}

// Init is used to initialize a BPF device for promiscuous sniffing.
// It also starts a goroutine to continuously read frames.
func (b *BPFSniffer) Init() error {
	var err error
	enable := 1

	// setup our read buffer
	if b.readBufLen == 0 {
		b.readBufLen, err = syscall.BpfBuflen(b.fd)
		if err != nil {
			panic(err)
		}
	} else {
		b.readBufLen, err = syscall.SetBpfBuflen(b.fd, b.readBufLen)
		if err != nil {
			panic(err)
		}
	}
	b.readBuffer = make([]byte, b.readBufLen)

	if b.bpfDeviceName == "" {
		b.pickBpfDevice()
	}

	err = syscall.SetBpfInterface(b.fd, b.sniffDeviceName)
	if err != nil {
		return err
	}

	// turning Immediate mode off should make the snffer
	// block when no packets are available.
	err = syscall.SetBpfImmediate(b.fd, enable)
	if err != nil {
		return err
	}

	// preserves the link level source address...
	// higher level protocol analyzers will not need this
	err = syscall.SetBpfHeadercmpl(b.fd, enable)
	if err != nil {
		return err
	}

	// forces the interface into promiscuous mode
	err = syscall.SetBpfPromisc(b.fd, enable)
	if err != nil {
		return err
	}

	return nil
}

func (b *BPFSniffer) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) {
	var err error
	if b.readBytesConsumed >= b.lastReadLen {
		b.lastReadLen, err = syscall.Read(b.fd, b.readBuffer)
		if err != nil {
			return nil, gopacket.CaptureInfo{}, err
		}
		b.readBytesConsumed = 0
	}
	hdr := (*unix.BpfHdr)(unsafe.Pointer(&b.readBuffer[b.readBytesConsumed]))
	frameStart := b.readBytesConsumed + int(hdr.Hdrlen)
	b.readBytesConsumed += bpfWordAlign(int(hdr.Hdrlen) + int(hdr.Caplen))
	rawFrame := b.readBuffer[frameStart : frameStart+int(hdr.Caplen)]
	captureInfo := gopacket.CaptureInfo{
		Timestamp:     time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000),
		CaptureLength: len(rawFrame),
		Length:        len(rawFrame),
	}
	return rawFrame, captureInfo, nil
}
<commit_msg>Fix Init, open bpf device first<commit_after>// +build darwin dragonfly freebsd netbsd openbsd

// Copyright 2012 Google, Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.

package bsdbpf

import (
	"github.com/google/gopacket"
	"golang.org/x/sys/unix"

	"fmt"
	"syscall"
	"time"
	"unsafe"
)

const wordSize = int(unsafe.Sizeof(uintptr(0)))

func bpfWordAlign(x int) int {
	return (((x) + (wordSize - 1)) &^ (wordSize - 1))
}

type TimedFrame struct {
	RawFrame  []byte
	Timestamp time.Time
}

type BPFSniffer struct {
	sniffDeviceName string
	bpfDeviceName string
	fd       int
	stopChan chan bool
	readChan chan TimedFrame
	readBuffer []byte
	readBufLen int
	lastReadLen int
	readBytesConsumed int
}

func NewBPFSniffer(sniffDeviceName, bpfDeviceName string, readBufLen int) *BPFSniffer {
	return &BPFSniffer{
		sniffDeviceName: sniffDeviceName,
		bpfDeviceName: bpfDeviceName,
		stopChan: make(chan bool, 0),
		readChan: make(chan TimedFrame, 0),
		readBufLen: readBufLen,
	}
}

func (b *BPFSniffer) Close() error {
	return syscall.Close(b.fd)
}

func (b *BPFSniffer) pickBpfDevice() {
	var err error
	for i := 0; i < 99; i++ {
		b.bpfDeviceName = fmt.Sprintf("/dev/bpf%d", i)
		b.fd, err = syscall.Open(b.bpfDeviceName, syscall.O_RDWR, 0)
		if err == nil {
			break
		}
	}
}

// Init is used to initialize a BPF device for promiscuous sniffing.
// It also starts a goroutine to continuously read frames.
func (b *BPFSniffer) Init() error {
	var err error
	enable := 1

	if b.bpfDeviceName == "" {
		b.pickBpfDevice()
	}

	// setup our read buffer
	if b.readBufLen == 0 {
		b.readBufLen, err = syscall.BpfBuflen(b.fd)
		if err != nil {
			panic(err)
		}
	} else {
		b.readBufLen, err = syscall.SetBpfBuflen(b.fd, b.readBufLen)
		if err != nil {
			panic(err)
		}
		var actualBufLen int
		actualBufLen, err = syscall.BpfBuflen(b.fd)
		if err != nil {
			panic(err)
		}
		fmt.Printf("actual buf len %d\n", actualBufLen)
	}
	b.readBuffer = make([]byte, b.readBufLen)

	err = syscall.SetBpfInterface(b.fd, b.sniffDeviceName)
	if err != nil {
		return err
	}

	// turning Immediate mode off should make the snffer
	// block when no packets are available.
	err = syscall.SetBpfImmediate(b.fd, enable)
	if err != nil {
		return err
	}

	// preserves the link level source address...
	// higher level protocol analyzers will not need this
	err = syscall.SetBpfHeadercmpl(b.fd, enable)
	if err != nil {
		return err
	}

	// forces the interface into promiscuous mode
	err = syscall.SetBpfPromisc(b.fd, enable)
	if err != nil {
		return err
	}

	return nil
}

func (b *BPFSniffer) ReadPacketData() ([]byte, gopacket.CaptureInfo, error) {
	var err error
	if b.readBytesConsumed >= b.lastReadLen {
		b.lastReadLen, err = syscall.Read(b.fd, b.readBuffer)
		if err != nil {
			return nil, gopacket.CaptureInfo{}, err
		}
		b.readBytesConsumed = 0
	}
	hdr := (*unix.BpfHdr)(unsafe.Pointer(&b.readBuffer[b.readBytesConsumed]))
	frameStart := b.readBytesConsumed + int(hdr.Hdrlen)
	b.readBytesConsumed += bpfWordAlign(int(hdr.Hdrlen) + int(hdr.Caplen))
	rawFrame := b.readBuffer[frameStart : frameStart+int(hdr.Caplen)]
	captureInfo := gopacket.CaptureInfo{
		Timestamp:     time.Unix(int64(hdr.Tstamp.Sec), int64(hdr.Tstamp.Usec)*1000),
		CaptureLength: len(rawFrame),
		Length:        len(rawFrame),
	}
	return rawFrame, captureInfo, nil
}
<|endoftext|>