<commit_before>package main

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

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

// ========== Mocked CmdMock struct as test helper for Dcm ==========

type CmdMock struct {
	// CmdMock extends Cmd
	Cmd

	// Fields from CmdMock
	name, dir string
	args, env []string
}

func (c *CmdMock) Exec(name string, args ...string) Executable {
	c.name = name
	c.args = args
	return c
}

func (c *CmdMock) Setdir(dir string) Executable {
	c.dir = dir
	return c
}

func (c *CmdMock) Setenv(env []string) Executable {
	c.env = env
	return c
}

func (c *CmdMock) Getenv() []string {
	return c.env
}

func (c *CmdMock) Run() error {
	switch c.name {
	case "git":
		if len(c.args) == 3 && c.args[0] == "clone" &&
			c.args[1] == "test-dcm-setup-error" {
			return errors.New("exit status 1")
		}
		if len(c.args) == 2 && c.args[0] == "checkout" &&
			c.args[1] == "test-dcm-setup-error" {
			return errors.New("exit status 1")
		}
	case "docker-compose":
		if c.dir == "/test/dcm/run/execute/error" {
			return errors.New("exit status 1")
		}
	case "/bin/bash":
		if len(c.args) == 1 &&
			c.args[0] == "test/dcm/run/init/error" {
			return errors.New("exit status 1")
		}
	}
	return nil
}

func (c *CmdMock) Out() ([]byte, error) {
	return []byte(""), nil
}

// ========== Here starts the real tests for Dcm ==========

func TestSetup(t *testing.T) {
	fixtures := []struct {
		name   string
		config yamlConfig
		code   int
		err    error
	}{
		{
			name: "Negative case for failing reading git repository config",
			config: yamlConfig{
				"service": yamlConfig{"build": "./build/dir"},
			},
			code: 1,
			err:  errors.New("Error reading git repository config for service [service]"),
		},
		{
			name: "Negative case for failing cloning git repository",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{"dcm.repository": "test-dcm-setup-error"},
				},
			},
			code: 1,
			err:  errors.New("Error cloning git repository for service [service]: exit status 1"),
		},
		{
			name: "Negative case for failing switching to pre-configured git branch",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.repository": "test-dcm-setup-ok",
						"dcm.branch":     "test-dcm-setup-error",
					},
				},
			},
			code: 1,
			err:  errors.New("exit status 1"),
		},
		{
			name: "Positive case, success",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.repository": "test-dcm-setup-ok",
						"dcm.branch":     "test-dcm-setup-ok",
					},
				},
			},
			code: 0,
			err:  nil,
		},
	}

	td, err := ioutil.TempDir("", "dcm")
	require.Nil(t, err)
	defer os.Remove(td)

	dcm := NewDcm(NewConfig(), []string{})
	dcm.Cmd = &CmdMock{}
	dcm.Config.Srv = td

	for n, test := range fixtures {
		dcm.Config.Config = test.config
		code, err := dcm.Setup()
		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
		if test.err != nil {
			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
		} else {
			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
		}
	}
}

func TestRunExecute(t *testing.T) {
	fixtures := []struct {
		name, dir string
		code      int
	}{
		{
			name: "Negative case for failing to run docker-compose command",
			dir:  "/test/dcm/run/execute/error",
			code: 1,
		},
		{
			name: "Positive case, success",
			dir:  "/test/dcm/run/execute/ok",
			code: 0,
		},
	}

	dcm := NewDcm(NewConfig(), []string{})
	dcm.Cmd = &CmdMock{}

	for n, test := range fixtures {
		dcm.Config.Dir = test.dir
		code, err := dcm.runExecute()
		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
		if test.code == 1 {
			assert.Error(t, err, "[%d: %s] Incorrect error returned", n, test.name)
		} else {
			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
		}
	}
}

func TestRunInit(t *testing.T) {
	fixtures := []struct {
		name   string
		config yamlConfig
		code   int
		err    error
	}{
		{
			name: "Negative case for config has no init script",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.test": "test",
					},
				},
			},
			code: 0,
			err:  nil,
		},
		{
			name: "Negative case for failing to exuecute init script",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.initscript": "test/dcm/run/init/error",
					},
				},
			},
			code: 1,
			err:  errors.New("Error executing init script [test/dcm/run/init/error] for service [service]: exit status 1"),
		},
		{
			name: "Positive case, success",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.initscript": "test/dcm/run/init/ok",
					},
				},
			},
			code: 0,
			err:  nil,
		},
	}

	dcm := NewDcm(NewConfig(), []string{})
	dcm.Cmd = &CmdMock{}

	for n, test := range fixtures {
		dcm.Config.Config = test.config
		code, err := dcm.runInit()
		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
		if test.err != nil {
			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
		} else {
			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
		}
	}
}

func TestDoForEachService(t *testing.T) {
	var (
		doSrv doForService
		code  int
		err   error
	)

	dcm := NewDcm(NewConfig(), []string{})

	// Bad fixture data
	fixtureBad := yamlConfig{
		"srv1": "config1",
		"srv2": "config2",
	}
	// Good fixture data
	fixtureGood := yamlConfig{
		"srv1": yamlConfig{
			"config": "value",
		},
		"srv2": yamlConfig{
			"config": "value",
		},
	}

	// Negative test case for failing with panic
	dcm.Config.Config = fixtureBad
	doSrv = func(service string, configs yamlConfig) (int, error) {
		return 0, nil
	}
	assert.Panics(t, func() { dcm.doForEachService(doSrv) })

	// Negative test case for failing with error
	dcm.Config.Config = fixtureGood
	doSrv = func(service string, configs yamlConfig) (int, error) {
		return 1, errors.New("Error")
	}
	assert.NotPanics(t, func() { dcm.doForEachService(doSrv) })
	code, err = dcm.doForEachService(doSrv)
	assert.Equal(t, 1, code)
	assert.Error(t, err)

	// Positive test case, success
	dcm.Config.Config = fixtureGood
	doSrv = func(service string, configs yamlConfig) (int, error) {
		return 0, nil
	}
	assert.NotPanics(t, func() { dcm.doForEachService(doSrv) })
	code, err = dcm.doForEachService(doSrv)
	assert.Equal(t, 0, code)
	assert.NoError(t, err)
}
<commit_msg>Added more empty tests to fill in for dcm<commit_after>package main

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

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

// ========== Mocked CmdMock struct as test helper for Dcm ==========

type CmdMock struct {
	// CmdMock extends Cmd
	Cmd

	// Fields from CmdMock
	name, dir string
	args, env []string
}

func (c *CmdMock) Exec(name string, args ...string) Executable {
	c.name = name
	c.args = args
	return c
}

func (c *CmdMock) Setdir(dir string) Executable {
	c.dir = dir
	return c
}

func (c *CmdMock) Setenv(env []string) Executable {
	c.env = env
	return c
}

func (c *CmdMock) Getenv() []string {
	return c.env
}

func (c *CmdMock) Run() error {
	switch c.name {
	case "git":
		if len(c.args) == 3 && c.args[0] == "clone" &&
			c.args[1] == "test-dcm-setup-error" {
			return errors.New("exit status 1")
		}
		if len(c.args) == 2 && c.args[0] == "checkout" &&
			c.args[1] == "test-dcm-setup-error" {
			return errors.New("exit status 1")
		}
	case "docker-compose":
		if c.dir == "/test/dcm/run/execute/error" {
			return errors.New("exit status 1")
		}
	case "/bin/bash":
		if len(c.args) == 1 &&
			c.args[0] == "test/dcm/run/init/error" {
			return errors.New("exit status 1")
		}
	}
	return nil
}

func (c *CmdMock) Out() ([]byte, error) {
	return []byte(""), nil
}

// ========== Here starts the real tests for Dcm ==========

func TestSetup(t *testing.T) {
	fixtures := []struct {
		name   string
		config yamlConfig
		code   int
		err    error
	}{
		{
			name: "Negative case for failing reading git repository config",
			config: yamlConfig{
				"service": yamlConfig{"build": "./build/dir"},
			},
			code: 1,
			err:  errors.New("Error reading git repository config for service [service]"),
		},
		{
			name: "Negative case for failing cloning git repository",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{"dcm.repository": "test-dcm-setup-error"},
				},
			},
			code: 1,
			err:  errors.New("Error cloning git repository for service [service]: exit status 1"),
		},
		{
			name: "Negative case for failing switching to pre-configured git branch",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.repository": "test-dcm-setup-ok",
						"dcm.branch":     "test-dcm-setup-error",
					},
				},
			},
			code: 1,
			err:  errors.New("exit status 1"),
		},
		{
			name: "Positive case, success",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.repository": "test-dcm-setup-ok",
						"dcm.branch":     "test-dcm-setup-ok",
					},
				},
			},
			code: 0,
			err:  nil,
		},
	}

	td, err := ioutil.TempDir("", "dcm")
	require.Nil(t, err)
	defer os.Remove(td)

	dcm := NewDcm(NewConfig(), []string{})
	dcm.Cmd = &CmdMock{}
	dcm.Config.Srv = td

	for n, test := range fixtures {
		dcm.Config.Config = test.config
		code, err := dcm.Setup()
		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
		if test.err != nil {
			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
		} else {
			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
		}
	}
}

func TestDoForEachService(t *testing.T) {
	var (
		doSrv doForService
		code  int
		err   error
	)

	dcm := NewDcm(NewConfig(), []string{})

	// Bad fixture data
	fixtureBad := yamlConfig{
		"srv1": "config1",
		"srv2": "config2",
	}
	// Good fixture data
	fixtureGood := yamlConfig{
		"srv1": yamlConfig{
			"config": "value",
		},
		"srv2": yamlConfig{
			"config": "value",
		},
	}

	// Negative test case for failing with panic
	dcm.Config.Config = fixtureBad
	doSrv = func(service string, configs yamlConfig) (int, error) {
		return 0, nil
	}
	assert.Panics(t, func() { dcm.doForEachService(doSrv) })

	// Negative test case for failing with error
	dcm.Config.Config = fixtureGood
	doSrv = func(service string, configs yamlConfig) (int, error) {
		return 1, errors.New("Error")
	}
	assert.NotPanics(t, func() { dcm.doForEachService(doSrv) })
	code, err = dcm.doForEachService(doSrv)
	assert.Equal(t, 1, code)
	assert.Error(t, err)

	// Positive test case, success
	dcm.Config.Config = fixtureGood
	doSrv = func(service string, configs yamlConfig) (int, error) {
		return 0, nil
	}
	assert.NotPanics(t, func() { dcm.doForEachService(doSrv) })
	code, err = dcm.doForEachService(doSrv)
	assert.Equal(t, 0, code)
	assert.NoError(t, err)
}

func TestRunExecute(t *testing.T) {
	fixtures := []struct {
		name, dir string
		code      int
	}{
		{
			name: "Negative case for failing to run docker-compose command",
			dir:  "/test/dcm/run/execute/error",
			code: 1,
		},
		{
			name: "Positive case, success",
			dir:  "/test/dcm/run/execute/ok",
			code: 0,
		},
	}

	dcm := NewDcm(NewConfig(), []string{})
	dcm.Cmd = &CmdMock{}

	for n, test := range fixtures {
		dcm.Config.Dir = test.dir
		code, err := dcm.runExecute()
		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
		if test.code == 1 {
			assert.Error(t, err, "[%d: %s] Incorrect error returned", n, test.name)
		} else {
			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
		}
	}
}

func TestRunInit(t *testing.T) {
	fixtures := []struct {
		name   string
		config yamlConfig
		code   int
		err    error
	}{
		{
			name: "Negative case for config has no init script",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.test": "test",
					},
				},
			},
			code: 0,
			err:  nil,
		},
		{
			name: "Negative case for failing to exuecute init script",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.initscript": "test/dcm/run/init/error",
					},
				},
			},
			code: 1,
			err:  errors.New("Error executing init script [test/dcm/run/init/error] for service [service]: exit status 1"),
		},
		{
			name: "Positive case, success",
			config: yamlConfig{
				"service": yamlConfig{
					"labels": yamlConfig{
						"dcm.initscript": "test/dcm/run/init/ok",
					},
				},
			},
			code: 0,
			err:  nil,
		},
	}

	dcm := NewDcm(NewConfig(), []string{})
	dcm.Cmd = &CmdMock{}

	for n, test := range fixtures {
		dcm.Config.Config = test.config
		code, err := dcm.runInit()
		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
		if test.err != nil {
			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
		} else {
			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
		}
	}
}

func TestDir(t *testing.T) {
}

func TestShell(t *testing.T) {
}

func TestGetContainerId(t *testing.T) {
}

func TestGetImageRepository(t *testing.T) {
}

func TestBranchForOne(t *testing.T) {
}

func TestUpdate(t *testing.T) {
}

func TestPurgeImages(t *testing.T) {
}

func TestPurgeContainers(t *testing.T) {
}
<|endoftext|>


<commit_before>package main

import ("github.com/nsf/termbox-go";"time";"strings")

// unicode constants
const (
	v = '\u2502'
	h = '\u2500'
	box_ul = '\u256d'
	box_ur = '\u256e'
	box_ll = '\u2570'
	box_lr = '\u256f'
	name = " SAMAYAM "
	FG = termbox.ColorRed
	BG = termbox.ColorBlack
	H_SPACE = 1
	H_LEN =5
	V_SPACE = 2
	V_LEN =5
)

type BOX struct {

	width,height int
	x_start,y_start int

}

type TASK struct {

	task string
	start time.Time
	end time.Time
}

type TASK_TREE struct {

	tree []TASK

}

func draw_string_box(fill string,x,y,padding int){

	actual_width := len(fill) + 2*padding
	actual_height :=  1 + 2*padding
	actual_start_x := x + padding
	actual_start_y := y + padding

	for i,j := range fill {
		termbox.SetCell(actual_start_x+i,actual_start_y,j,FG,BG)
	}

	rect := BOX{ x_start:x,y_start:y,width:actual_width,height:actual_height}
	rect.draw_mainbox()
}

func draw_vertical(x,y,h int){

	for i := y ; i <= y+h-1 ; i++ {
		termbox.SetCell(x,i,v,FG,BG)
	}
}

func draw_horizontal(x,y,w int){

	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,FG,BG)
	}
}

func box_filler(x,y,w int){
	// debug function
	a := [5] termbox.Attribute{ termbox.ColorRed,termbox.ColorGreen,termbox.ColorYellow }
	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,termbox.ColorWhite,a[i%3])
	}
}

func (r *BOX) draw_mainbox(){

	termbox.SetCell(r.x_start,r.y_start,box_ul,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start,box_ur,FG,BG)

	termbox.SetCell(r.x_start,r.y_start+r.height-1,box_ll,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start+r.height-1,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start+r.height-1,box_lr,FG,BG)

	draw_vertical(r.x_start,r.y_start+1,r.height-2)
	draw_vertical(r.x_start+r.width-1,r.y_start+1,r.height-2)

}

func ( t *TASK) draw_task (x,y,padding int){
	s := []string{t.task,t.start.String()}
	string_to_draw :=  strings.Join(s, "    " )
	draw_string_box(string_to_draw,x,y,padding )

}

func ( t* TASK_TREE) draw_tree(){

	//length := len(t.tree)
	for i,j :=range(t.tree){
		box_x := H_SPACE + H_LEN
		box_y := V_SPACE + i*V_LEN
		j.draw_task(box_x,box_y,1)
		draw_horizontal(H_SPACE,box_y+1,H_LEN)
		if i>=1 {
			draw_vertical(H_SPACE,box_y+1-V_LEN,V_LEN)
		}
	}

}

func main(){

	errit := termbox.Init()
	if errit != nil {
		panic(" Trouble somewhere")
	} else {
		//x := BOX{width:20,x_start:3,y_start:3,height:20}
		//x.draw_mainbox()
		for  {
			task_1 := TASK_TREE{tree:[]TASK{  TASK{task:" take meeko for a walk",start:time.Now()}, TASK{task:" finish samayam",start:time.Now()}, TASK{task:" finish python",start:time.Now()}}}
			task_1.draw_tree()
			termbox.Flush()
			g := termbox.PollEvent()
			if g.Ch == 'z'{
				break
			}
			termbox.Clear(FG,BG)
		}
	}
	defer termbox.Close()

}

<commit_msg>added event loop .. a messy one .. :(<commit_after>package main

import ("github.com/nsf/termbox-go";"time";"strings")

// unicode constants
const (
	v = '\u2502'
	h = '\u2500'
	box_ul = '\u256d'
	box_ur = '\u256e'
	box_ll = '\u2570'
	box_lr = '\u256f'
	name = " SAMAYAM "
	FG = termbox.ColorWhite
	BG = termbox.ColorBlack
	H_SPACE = 1
	H_LEN =5
	V_SPACE = 2
	V_LEN =5
)

type BOX struct {

	width,height int
	x_start,y_start int

}

type INSERT struct {

	x,y int
	buffer string

}

type TASK struct {

	task string
	start time.Time
	end time.Time
}

func draw_string(fill string,x,y int){

	for i,j := range fill {
			termbox.SetCell(x+i,y,j,FG,BG)
		}

}

func draw_string_box(fill string,x,y,padding int){

	actual_width := len(fill) + 2*padding
	actual_height :=  1 + 2*padding
	actual_start_x := x + padding
	actual_start_y := y + padding

	draw_string(fill,actual_start_x,actual_start_y)

	rect := BOX{ x_start:x,y_start:y,width:actual_width,height:actual_height}
	rect.draw_mainbox()
}

func draw_vertical(x,y,h int){

	for i := y ; i <= y+h-1 ; i++ {
		termbox.SetCell(x,i,v,FG,BG)
	}
}

func draw_horizontal(x,y,w int){

	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,FG,BG)
	}
}

func box_filler(x,y,w int){
	// debug function
	a := [5] termbox.Attribute{ termbox.ColorRed,termbox.ColorGreen,termbox.ColorYellow }
	for i := x ; i <= x+w-1 ; i++ {
		termbox.SetCell(i,y,h,termbox.ColorWhite,a[i%3])
	}
}

func (r *BOX) draw_mainbox(){

	termbox.SetCell(r.x_start,r.y_start,box_ul,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start,box_ur,FG,BG)

	termbox.SetCell(r.x_start,r.y_start+r.height-1,box_ll,FG,BG)
	draw_horizontal(r.x_start+1,r.y_start+r.height-1,r.width-2)
	termbox.SetCell(r.x_start+r.width-1,r.y_start+r.height-1,box_lr,FG,BG)

	draw_vertical(r.x_start,r.y_start+1,r.height-2)
	draw_vertical(r.x_start+r.width-1,r.y_start+1,r.height-2)

}

func ( t *TASK) draw_task (x,y,padding int){
	s := []string{t.task,t.start.String()}
	string_to_draw :=  strings.Join(s, "    " )
	draw_string_box(string_to_draw,x,y,padding )

}

func draw_tree(tree []TASK){

	//length := len(t.tree)
	for i,j :=range(tree){
		box_x := H_SPACE + H_LEN
		box_y := V_SPACE + i*V_LEN
		j.draw_task(box_x,box_y,1)
		draw_horizontal(H_SPACE,box_y+1,H_LEN)
		if i>=1 {
			draw_vertical(H_SPACE,box_y+1-V_LEN,V_LEN)
		}
	}

}

func main(){

	errit := termbox.Init()
	screen_w,screen_h := termbox.Size()

	if errit != nil {
		panic(" Trouble somewhere")
	} else {
		//x := BOX{width:20,x_start:3,y_start:3,height:20}
		//x.draw_mainbox()
		for  {
			task_1 := []TASK{ TASK{task:" take meeko for a walk",start:time.Now()}, TASK{task:" finish samayam",start:time.Now()}, TASK{task:" finish python",start:time.Now()}}
			draw_tree(task_1)

			commandbox := BOX{x_start:H_SPACE,y_start:screen_h-V_SPACE-3,width:screen_w-H_SPACE-1,height:3}
			commandbox.draw_mainbox()
			draw_string("COMMAND : ",H_SPACE+2,screen_h-V_SPACE-2)
			draw_string(" [ Press i to enter commands ][ Press h for help ][ Press z to quit ] ",H_SPACE+1,screen_h-1)
			termbox.Flush()
			g := termbox.PollEvent()
			ogg := INSERT{x:H_SPACE+2+len("COMMAND :"),y:screen_h-V_SPACE-2,buffer:""}
			if g.Ch == 'z'{
				break
			} else if g.Ch == 'i'{
				for {

					g :=  termbox.PollEvent()
					if g.Key == termbox.KeyEnter {
						break
					}else if g.Key == termbox.KeySpace{
						break
					}else{
						ogg.buffer+=string(g.Ch)
						draw_string(ogg.buffer,ogg.x,ogg.y)
						termbox.Flush()
					}
				}
			}
			termbox.Clear(FG,BG)
		}
	}
	defer termbox.Close()

}

<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"

	"path"

	"net/http"

	"time"

	"gopkg.in/urfave/cli.v1"
)

func main() {

	mapboxHTTPClient := &http.Client{
		Timeout: time.Second * 30,
	}

	app := cli.NewApp()
	app.Name = "mapuploader"
	app.Usage = "uploads data to mapbox servers"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "mapbox_key, mk",
			Usage:  "the key for mapbox api access",
			EnvVar: "MAPBOX_KEY",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "upload",
			Aliases: []string{"u"},
			Usage:   "uploads the geojson or tile to mapbox",
			Action: func(c *cli.Context) error {
				fmt.Println("uploading file: ", c.Args().First())
				getMapboxCredentials("holahola", "juanjcsr", mapboxHTTPClient)
				return nil
			},
		},
	}

	app.Action = func(c *cli.Context) error {
		fmt.Println("hola :D")
		return nil
	}
	app.Run(os.Args)
}

// MapboxURL has the remote mapbox endpoint
const MapboxURL = "https://api.mapbox.com"

type mapboxCredentials struct {
	AccessKeyID     string
	Bucket          string
	Key             string
	SecretAccessKey string
	SessionToken    string
	URL             string
}

func getMapboxCredentials(apiKey string, userName string, mapboxClient *http.Client) {
	baseURL, _ := url.Parse(MapboxURL)
	uploadsURLSegment := fmt.Sprintf("/uploads/v1/%s/credentials", userName)
	parameters := url.Values{}
	parameters.Add("access_token", apiKey)
	baseURL.Path = path.Join(baseURL.Path, uploadsURLSegment)
	baseURL.RawQuery = parameters.Encode()
	fmt.Println(baseURL.String())

	response, err := mapboxClient.Get(baseURL.String())
	defer response.Body.Close()
	if err != nil {
		credentials := new(mapboxCredentials)
		if err := json.NewDecoder(response.Body).Decode(credentials); err != nil {
			fmt.Println(credentials)
		}
	}

}
<commit_msg>add mapboxkey and mapbox username validation<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"time"

	"path"

	"net/http"

	"gopkg.in/urfave/cli.v1"
)

func main() {

	mapboxHTTPClient := &http.Client{
		Timeout: time.Second * 30,
	}

	var mapboxAPIKey string
	var mapboxUserName string

	app := cli.NewApp()
	app.Name = "mapuploader"
	app.Usage = "uploads data to mapbox servers"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:        "mapbox_key, mk",
			Usage:       "the key for mapbox api access",
			EnvVar:      "MAPBOX_KEY",
			Destination: &mapboxAPIKey,
		},
		cli.StringFlag{
			Name:        "mapbox_user, u",
			Usage:       "the mapbox username",
			Destination: &mapboxUserName,
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "upload",
			Aliases: []string{"u"},
			Usage:   "uploads the geojson or tile to mapbox",
			Action: func(c *cli.Context) error {
				fmt.Println("uploading file: ", c.Args().First())
				if mapboxAPIKey == "" || mapboxUserName == "" {
					return cli.NewExitError("you need to provide mapbox access tokens", 1)
				}
				getMapboxCredentials(mapboxAPIKey, mapboxUserName, mapboxHTTPClient)
				return nil
			},
		},
	}

	app.Action = func(c *cli.Context) error {
		fmt.Println("hola :D")
		return nil
	}
	app.Run(os.Args)
}

// MapboxURL has the remote mapbox endpoint
const MapboxURL = "https://api.mapbox.com"

// MapboxCredentials holds the keys needed to upload stuff
type MapboxCredentials struct {
	AccessKeyID     string
	Bucket          string
	Key             string
	SecretAccessKey string
	SessionToken    string
	URL             string
}

func getMapboxCredentials(apiKey string, userName string, mapboxClient *http.Client) {
	baseURL, _ := url.Parse(MapboxURL)
	uploadsURLSegment := fmt.Sprintf("/uploads/v1/%s/credentials", userName)
	parameters := url.Values{}
	parameters.Add("access_token", apiKey)
	baseURL.Path = path.Join(baseURL.Path, uploadsURLSegment)
	baseURL.RawQuery = parameters.Encode()
	fmt.Println(baseURL.String())

	response, err := mapboxClient.Get(baseURL.String())
	defer response.Body.Close()
	if err == nil {
		credentials := new(MapboxCredentials)
		if err := json.NewDecoder(response.Body).Decode(credentials); err == nil {
			fmt.Println(credentials)
		}
	}

}
<|endoftext|>


<commit_before>package bites

import (
	"bytes"
	"unicode/utf8"
)

type Bites []byte

const extendShortLen = 512

var extendShort [extendShortLen]byte

// Returns an empty slice.
func Empty() Bites {
	return []byte{}
}

// Make a string out of the slice.
// This unavoidably allocates.
func (b Bites) String() string {
	return string(b)
}

// Returns b with at least s capacity left.
// TODO: short path
func (b Bites) Capacity(s int) Bites {
	orig := len(b)
	b = append(b, make([]byte, s)...)
	b = b[:orig]
	return b
}

// Extend b by s, return the complete, extended, slice.
// This may cause an extra allocation if s is much larger than cap-len.
func (b Bites) Extend(s int, zero bool) Bites {
	l := len(b)
	e := l
	if l+s <= cap(b) {
		// Extension fits in cap
		b = b[:l+s]
		e += s
	} else {
		// Extension does not fit, use up all cap first
		b = b[:cap(b)]
		s -= cap(b) - l
		e = len(b)
		if s <= extendShortLen {
			// Short append, alloc-free
			b = append(b, extendShort[:s]...)
			e = len(b)
		} else {
			// Long append, allocates
			b = append(b, make([]byte, s)...)
		}
	}
	if zero {
		x := b[l:e]
		for len(x) > 0 {
			x = x[copy(x, extendShort[:]):]
		}
	}
	return b
}

// Set length to 0.
func (b Bites) Reuse() Bites {
	return b[:0]
}

// Append the given slice.
func (b Bites) PutSlice(slice []byte) Bites {
	return append(b, slice...)
}

// Get a slice of the given size.
func (b Bites) GetSlice(slice *[]byte, size int) Bites {
	*slice = b[:size]
	return b[size:]
}

// Get a slice of the given size.
func (b Bites) GetSliceCopy(slice []byte) Bites {
	return b[copy(slice, b):]
}

// Append the given string.
func (b Bites) PutString(str string) Bites {
	return append(b, str...)
}

// Append one byte.
func (b Bites) PutByte(byt byte) Bites {
	return append(b, byt)
}

// Get one byte.
func (b Bites) GetByte(byt *byte) Bites {
	*byt = b[0]
	return b[1:]
}

// Append the given rune as UTF8.
// If the rune is not valid UTF8, it panics with an ErrorInvalidRune.
func (b Bites) PutRune(r rune) Bites {
	l := utf8.RuneLen(r)
	if l == -1 {
		panic(ErrorInvalidRune(r))
	}
	b = b.Extend(l, false)
	utf8.EncodeRune(b.Last(l), r)
	return b
}

// Make an exact copy and return it.
// This will almost certainly be allocated on the heap.
func (b Bites) Clone() Bites {
	clone := make(Bites, len(b), len(b))
	copy(clone, b)
	return clone
}

// Return a slice containing the last s bytes.
func (b Bites) Last(s int) Bites {
	return b[len(b)-s:]
}

// Return a slice without the first s bytes.
func (b Bites) Skip(s int) Bites {
	return b[s:]
}

// Return a slice with the last s bytes snipped off.
func (b Bites) Snip(s int) Bites {
	return b[:len(b)-s]
}

// Split the slice into the first s bytes and the rest.
func (b Bites) Split(s int) (Bites, Bites) {
	return b[:s], b[s:]
}

// Set all bytes to s.
func (b Bites) Set(s byte) Bites {
	for i := range b {
		b[i] = s
	}
	return b
}

// Set all bytes to 0.
// This is much faster than Set(0).
func (b Bites) Zero() Bites {
	x := b
	for len(x) > 0 {
		x = x[copy(x, extendShort[:]):]
	}
	return b
}

// True if both slices are exactly equal
func (b Bites) Equal(c Bites) bool {
	return bytes.Compare(b, c) == 0
}

// True if the slice is equal to the given string
// TODO: Make sure this doesn't allocate.
func (b Bites) Sequal(str string) bool {
	return b.Equal(Bites(str))
}
<commit_msg>Improve Extend comments<commit_after>package bites

import (
	"bytes"
	"unicode/utf8"
)

type Bites []byte

const extendShortLen = 512

var extendShort [extendShortLen]byte

// Returns an empty slice.
func Empty() Bites {
	return []byte{}
}

// Make a string out of the slice.
// This unavoidably allocates.
func (b Bites) String() string {
	return string(b)
}

// Returns b with at least s capacity left.
// TODO: short path
func (b Bites) Capacity(s int) Bites {
	orig := len(b)
	b = append(b, make([]byte, s)...)
	b = b[:orig]
	return b
}

// Extend b by s, return the complete, extended, slice.
// If zero is true, the extension bytes are set to 0, otherwise their content is left as-is.
// An allocation (new backing array) will occur if s is larger than cap-len.
// An extra allocation (for a temporary slice) will occur if s is much larger (by 512 bytes) than cap-len.
func (b Bites) Extend(s int, zero bool) Bites {
	l := len(b)
	e := l
	if l+s <= cap(b) {
		// Extension fits in cap, no allocation.
		b = b[:l+s]
		e += s
	} else {
		// Extension does not fit, use up all cap first
		b = b[:cap(b)]
		s -= cap(b) - l
		e = cap(b)
		if s <= extendShortLen {
			// Short append, must allocate new backing array.
			b = append(b, extendShort[:s]...)
			e = len(b)
		} else {
			// Long append, must allocate new backing array and temporary slice.
			b = append(b, make([]byte, s)...)
		}
	}
	if zero {
		x := b[l:e]
		for len(x) > 0 {
			x = x[copy(x, extendShort[:]):]
		}
	}
	return b
}

// Set length to 0.
func (b Bites) Reuse() Bites {
	return b[:0]
}

// Append the given slice.
func (b Bites) PutSlice(slice []byte) Bites {
	return append(b, slice...)
}

// Get a slice of the given size.
func (b Bites) GetSlice(slice *[]byte, size int) Bites {
	*slice = b[:size]
	return b[size:]
}

// Get a slice of the given size.
func (b Bites) GetSliceCopy(slice []byte) Bites {
	return b[copy(slice, b):]
}

// Append the given string.
func (b Bites) PutString(str string) Bites {
	return append(b, str...)
}

// Append one byte.
func (b Bites) PutByte(byt byte) Bites {
	return append(b, byt)
}

// Get one byte.
func (b Bites) GetByte(byt *byte) Bites {
	*byt = b[0]
	return b[1:]
}

// Append the given rune as UTF8.
// If the rune is not valid UTF8, it panics with an ErrorInvalidRune.
func (b Bites) PutRune(r rune) Bites {
	l := utf8.RuneLen(r)
	if l == -1 {
		panic(ErrorInvalidRune(r))
	}
	b = b.Extend(l, false)
	utf8.EncodeRune(b.Last(l), r)
	return b
}

// Make an exact copy and return it.
// This will almost certainly be allocated on the heap.
func (b Bites) Clone() Bites {
	clone := make(Bites, len(b), len(b))
	copy(clone, b)
	return clone
}

// Return a slice containing the last s bytes.
func (b Bites) Last(s int) Bites {
	return b[len(b)-s:]
}

// Return a slice without the first s bytes.
func (b Bites) Skip(s int) Bites {
	return b[s:]
}

// Return a slice with the last s bytes snipped off.
func (b Bites) Snip(s int) Bites {
	return b[:len(b)-s]
}

// Split the slice into the first s bytes and the rest.
func (b Bites) Split(s int) (Bites, Bites) {
	return b[:s], b[s:]
}

// Set all bytes to s.
func (b Bites) Set(s byte) Bites {
	for i := range b {
		b[i] = s
	}
	return b
}

// Set all bytes to 0.
// This is much faster than Set(0).
func (b Bites) Zero() Bites {
	x := b
	for len(x) > 0 {
		x = x[copy(x, extendShort[:]):]
	}
	return b
}

// True if both slices are exactly equal
func (b Bites) Equal(c Bites) bool {
	return bytes.Compare(b, c) == 0
}

// True if the slice is equal to the given string
// TODO: Make sure this doesn't allocate.
func (b Bites) Sequal(str string) bool {
	return b.Equal(Bites(str))
}
<|endoftext|>


<commit_before>package matrix

// Retrieve value at [row, col] safely.
func (A *Matrix) SafeGet(row, col int) (float64, error) {

	if A.isOutOfBounds(row, col) {
		return 0, ErrOutOfBounds
	}

	return A.Get(row, col), nil
}

// Multiplies 2 matricies with each other safely.
// Returns a new matrix.
func (A *Matrix) SafeMul(B *Matrix) (*Matrix, error) {
	if !isSquare(A, B) {
		return nil, ErrIncompatibleSizes
	}

	return A.Mul(B), nil
}

// Set the element at [row, col] to val safely.
func (A *Matrix) SafeSet(row, col int, val float64) error {
	if A.isOutOfBounds(row, col) {
		return ErrOutOfBounds
	}

	A.Set(row, col, val)

	return nil
}

// Calculates the standard scalar product of 2 matrixies safely.
// Returns a new matrix.
func (A *Matrix) SafeDot(B *Matrix) (*Matrix, error) {
	if !sameSize(A, B) {
		return nil, ErrIncompatibleSizes
	}

	return A.Dot(B), nil
}

// Insert the given rows into the matrix, returning a new matrix.
// Passing 0 as the second argument is like making the
// passed rows the first few, whereas passing Rows() is like appending
// the additional rows to the matrix.
func (A *Matrix) SafeInsertRows(rows *Matrix, afterRow int) (*Matrix, error) {
	if rows.cols != A.cols {
		return nil, ErrIncompatibleSizes
	}

	if afterRow < 0 || afterRow > A.rows {
		return nil, ErrInsertionOutOfBounds
	}

	return A.InsertRows(rows, afterRow), nil
}

func (A *Matrix) SafeInsertColumns(cols *Matrix, afterCol int) (*Matrix, error) {
	if cols.rows != A.rows {
		return nil, ErrIncompatibleSizes
	}

	if afterCol < 0 || afterCol > A.cols {
		return nil, ErrInsertionOutOfBounds
	}

	return A.InsertColumns(cols, afterCol), nil
}

func (A *Matrix) isOutOfBounds(row, col int) bool {
	index := (row-1)*A.cols + col - 1

	if index >= len(A.values) {
		return true
	}

	return false
}

func sameSize(A, B *Matrix) bool {
	return A.rows == B.rows && A.cols == B.cols
}

func isSquare(A, B *Matrix) bool {
	return A.cols == B.rows
}
<commit_msg>added documentation for SafeInsertColumns<commit_after>package matrix

// Retrieve value at [row, col] safely.
func (A *Matrix) SafeGet(row, col int) (float64, error) {

	if A.isOutOfBounds(row, col) {
		return 0, ErrOutOfBounds
	}

	return A.Get(row, col), nil
}

// Multiplies 2 matricies with each other safely.
// Returns a new matrix.
func (A *Matrix) SafeMul(B *Matrix) (*Matrix, error) {
	if !isSquare(A, B) {
		return nil, ErrIncompatibleSizes
	}

	return A.Mul(B), nil
}

// Set the element at [row, col] to val safely.
func (A *Matrix) SafeSet(row, col int, val float64) error {
	if A.isOutOfBounds(row, col) {
		return ErrOutOfBounds
	}

	A.Set(row, col, val)

	return nil
}

// Calculates the standard scalar product of 2 matrixies safely.
// Returns a new matrix.
func (A *Matrix) SafeDot(B *Matrix) (*Matrix, error) {
	if !sameSize(A, B) {
		return nil, ErrIncompatibleSizes
	}

	return A.Dot(B), nil
}

// Insert the given rows into the matrix, returning a new matrix.
// Passing 0 as the second argument is like making the
// passed rows the first few, whereas passing Rows() is like appending
// the additional rows to the matrix.
func (A *Matrix) SafeInsertRows(rows *Matrix, afterRow int) (*Matrix, error) {
	if rows.cols != A.cols {
		return nil, ErrIncompatibleSizes
	}

	if afterRow < 0 || afterRow > A.rows {
		return nil, ErrInsertionOutOfBounds
	}

	return A.InsertRows(rows, afterRow), nil
}

// Insert the given columns into the matrix, returning a new matrix.
// Passing 0 as the second argument is like making the
// passed columns the first few (on the left), whereas passing Columns() is like appending
// the additional columns to the matrix (on the right).
func (A *Matrix) SafeInsertColumns(cols *Matrix, afterCol int) (*Matrix, error) {
	if cols.rows != A.rows {
		return nil, ErrIncompatibleSizes
	}

	if afterCol < 0 || afterCol > A.cols {
		return nil, ErrInsertionOutOfBounds
	}

	return A.InsertColumns(cols, afterCol), nil
}

func (A *Matrix) isOutOfBounds(row, col int) bool {
	index := (row-1)*A.cols + col - 1

	if index >= len(A.values) {
		return true
	}

	return false
}

func sameSize(A, B *Matrix) bool {
	return A.rows == B.rows && A.cols == B.cols
}

func isSquare(A, B *Matrix) bool {
	return A.cols == B.rows
}
<|endoftext|>


<commit_before>package mcore

import (
	"fmt"
)

// print any type value
func Print(v ...interface{}) {
	fmt.Print(v...)
}

func Printf(format string, v ...interface{}) {
	fmt.Printf(format, v...)
}

// after print, start a new line
func Println(v ...interface{}) {
	fmt.Println(v...)
}

// each item in array start a new line.
func PrintStringArray(vs []string) {
	for _, v := range vs {
		fmt.Printf("%v\n", v)
	}
}

// inpect model and print
// model should be a struct
func PrintModel(model interface{}) {
	fmt.Println()
	fmt.Printf("%+v", model)
	fmt.Println()
}

func PrintModels(models []interface{}) {
	for _, v := range models {
		PrintModel(v)
	}
}
<commit_msg>fix print<commit_after>package mcore

import (
	"fmt"
	"reflect"
)

// print any type value
func Print(v ...interface{}) {
	fmt.Print(v...)
}

func Printf(format string, v ...interface{}) {
	fmt.Printf(format, v...)
}

// after print, start a new line
func Println(v ...interface{}) {
	fmt.Println(v...)
}

// each item in array start a new line.
func PrintStringArray(vs []string) {
	for _, v := range vs {
		fmt.Printf("%v\n", v)
	}
}

// inpect model and print
// model should be a struct
func PrintModel(model interface{}) {
	fmt.Println()
	fmt.Printf("%+v", model)
	fmt.Println()
}

// PrintModels .
func PrintModels(models []interface{}) {
	for _, v := range models {
		PrintModel(v)
	}
}

// PrintRows .
func PrintRows(model interface{}, include, exclude string) {
	v := reflect.ValueOf(model)
	if v.Kind() != reflect.Slice {
		fmt.Printf("Not a slice\n")
		return
	}
	fs := GetUsedArrayFields(model, include, exclude)
	for _, f := range fs {
		fmt.Printf("%s\t", f)
	}
	fmt.Printf("\n")
	// if not a slice, return input arg
	for i := 0; i < v.Len(); i++ {
		row := v.Index(0).Interface()
		for _, f := range fs {
			fv := GetFieldValue(row, f)
			fmt.Printf("%v\t", fv)
		}
		fmt.Printf("\n")
	}
	fmt.Printf("\n")
}
<|endoftext|>


<commit_before>package dagr

import (
	"encoding/json"
	"io"
	"sort"
	"sync"
	"time"
)

type Tags map[string]string

// Dup clones the Tags map, t. If t is nil, it returns nil.
func (t Tags) Dup() Tags {
	if t == nil {
		return nil
	}

	d := make(Tags, len(t))
	for name, tag := range t {
		d[name] = tag
	}
	return d
}

type Fields map[string]Field

// Dup clones the Fields map. If deep is true, it will also duplicate the fields held, creating new field instances,
// otherwise it retains its references to the Fields held by fs. If fs is nil, it returns nil.
func (fs Fields) Dup(deep bool) Fields {
	if fs == nil {
		return nil
	}

	d := make(Fields, len(fs))
	for name, field := range fs {
		if deep {
			field = field.Dup()
		}
		d[name] = field
	}
	return d
}

// Measurement defines the minimum interface for a measurement that could be passed to InfluxDB. All measurements must
// have a key and a minimum of one field. Tags are optional.
//
// Unless stated otherwise, the results of Tags and Fields are considered immutable.
type Measurement interface {
	Key() string
	Tags() Tags
	Fields() Fields
}

// TimeMeasurement is a measurement that has a known, known, fixed time. It can be considered a measurement that is
// fixed to a specific point in time. TimeMeasurements are not necessarily read-only and may, for example, return
// time.Now(). In general, it is better to not implement TimeMeasurement if your measurement is returning time.Now().
type TimeMeasurement interface {
	Time() time.Time

	Measurement
}

type fixedField struct {
	prefix []byte
	fields []fixedField
}

// Point is a single Measurement as understood by InfluxDB.
type Point struct {
	key        string
	tagOrder   []string
	fieldOrder []string
	tags       map[string]string
	fields     map[string]Field
	m          sync.RWMutex
}

var _ = Measurement((*Point)(nil))

// NewPoint allocates a new Point with the given key, tags, and fields. If key is empty, NewPoint panics. If fields is
// empty, the point cannot be written until it has at least one field.
func NewPoint(key string, tags Tags, fields Fields) *Point {
	if key == "" {
		panic("dagr.NewPoint: key is empty")
	}

	p := &Point{
		key:    key,
		tags:   make(map[string]string, len(tags)),
		fields: make(map[string]Field, len(fields)),
	}

	for name, tag := range tags {
		p.addTag(name, tag)
	}

	for name, field := range fields {
		p.addField(name, field)
	}

	return p
}

// WriteTo writes the point to the given writer, w. If an error occurs while building the point, it writes nothing and
// return an error. If the point has no fields, it returns the error ErrNoFields.
func (p *Point) WriteTo(w io.Writer) (int64, error) {
	buf := getBuffer(w)
	p.m.RLock()
	defer func() {
		p.m.RUnlock()
		putBuffer(buf)
	}()

	buf.WriteString(tagEscaper.Replace(p.key))
	writeTags(buf, p.tags, p.tagOrder)

	buf.WriteByte(' ')
	if err := writeFields(buf, p.fields, p.fieldOrder); err != nil {
		buf.Truncate(int(buf.head))
		return 0, err
	}

	buf.WriteByte(' ')
	writeTimestamp(buf, clock.Now())
	buf.WriteByte('\n')

	return buf.WriteTo(w)
}

// Key returns the point's key.
func (p *Point) Key() string {
	return p.key
}

// Compiled returns a compiled form of the point. The resulting Measurement is immutable except for its field values.
// New fields may not be added and it will not return a key, tags, or values. The compiled form of a point is only
// useful to improve write times on points when necessary. If the point has no fields, it returns nil, as the point is
// not valid to write.
func (p *Point) Compiled() Measurement {
	p.m.RLock()
	p.m.RUnlock()

	if len(p.fieldOrder) == 0 {
		return nil
	}
	return p.compile()
}

func (p *Point) compile() compiledPoint {
	var c compiledPoint
	buf := getBuffer(nil)
	defer putBuffer(buf)

	// Write key
	buf.WriteString(tagEscaper.Replace(p.key))
	// Write tags
	writeTags(buf, p.tags, p.tagOrder)
	c.lead = buf.Len()
	// Write field names
	var pre byte = ' '
	fields := make([]compiledField, len(p.fieldOrder))
	for i, name := range p.fieldOrder {
		field := p.fields[name]

		from := buf.Len()
		buf.WriteByte(pre)
		pre = ','

		buf.WriteString(tagEscaper.Replace(name))
		buf.WriteByte('=')

		to := buf.Len()
		if i == 0 {
			from = to
			c.lead = to
		}
		fields[i] = compiledField{from, to, field}
	}

	c.prefix = append([]byte(nil), buf.Bytes()...)
	c.fields = fields

	return c
}

// Tags

func (p *Point) addTag(name, value string) {
	_, exists := p.tags[name]
	p.tags[name] = value
	if exists {
		return
	}
	p.tagOrder = insertOrderedString(p.tagOrder, name)
}

// SetTag sets a tag on the point with the given name and value. If the value is empty, the tag is removed. If the name
// is empty, the call is a no-op. It is safe to call SetTag from concurrent goroutines.
func (p *Point) SetTag(name, value string) {
	if name == "" {
		return
	} else if value == "" {
		p.RemoveTag(name)
		return
	}

	p.m.Lock()
	defer p.m.Unlock()
	p.addTag(name, value)
}

// RemoveTag removes the tag with the given name from the point. If name is empty, the call is a no-op. It is safe to
// call from concurrent goroutines.
func (p *Point) RemoveTag(name string) {
	if name == "" {
		return
	}

	p.m.Lock()
	defer p.m.Unlock()

	if _, ok := p.tags[name]; !ok {
		// No tag
		return
	}

	delete(p.tags, name)
	for i, tname := range p.tagOrder {
		if name == tname {
			copy(p.tagOrder[i:], p.tagOrder[i+1:])
			p.tagOrder = p.tagOrder[:len(p.tagOrder)-1]
			break
		}
	}
}

// Tags returns a copy of the point's tags as a map of names to values. Names and values are not escaped. It is safe to
// copy and modify the result of this method.
func (p *Point) Tags() Tags {
	p.m.RLock()
	defer p.m.RUnlock()
	return Tags(p.tags).Dup()
}

// Fields

// insertOrderedString inserts str in the ordered slice of strings. It assumes that all elements of slice are unique,
// though this isn't necessarily important except that having non-unique names would result in Point having a memory
// leak.
func insertOrderedString(slice []string, str string) []string {
	n := len(slice)
	if idx := sort.SearchStrings(slice, str); idx < n {
		slice = append(slice, "")
		copy(slice[idx+1:], slice[idx:n])
		slice[idx] = str
	} else {
		slice = append(slice, str)
	}
	return slice
}

func (p *Point) addField(name string, value Field) {
	_, exists := p.fields[name]
	p.fields[name] = value
	if exists {
		return
	}
	p.fieldOrder = insertOrderedString(p.fieldOrder, name)
}

// SetField sets a field with the given name and value. If name is empty, the call is a no-op. If value is nil, it
// removes the field.
func (p *Point) SetField(name string, value Field) {
	if name == "" {
		return
	} else if value == nil {
		p.RemoveField(name)
		return
	}

	p.m.Lock()
	defer p.m.Unlock()
	p.addField(name, value)
}

// RemoveField removes a field with the given name. If name is empty, the call is a no-op. It is safe to call
// RemoveField from concurrent goroutines.
func (p *Point) RemoveField(name string) {
	if name == "" {
		return
	}

	p.m.Lock()
	defer p.m.Unlock()

	if _, ok := p.fields[name]; !ok {
		// No field
		return
	}

	delete(p.fields, name)
	for i, fname := range p.fieldOrder {
		if name == fname {
			copy(p.fieldOrder[i:], p.fieldOrder[i+1:])
			p.fieldOrder = p.fieldOrder[:len(p.fieldOrder)-1]
			break
		}
	}
}

func (p *Point) dupFields() Fields {
	return Fields(p.fields).Dup(false)
}

// Fields returns a map of the point's fields. This map is a copy of the point's state and may be modified without
// affecting the point. The fields themselves are those held by the point, however, and modifying them will modify the
// point's state.
func (p *Point) Fields() Fields {
	p.m.RLock()
	defer p.m.RUnlock()
	return Fields(p.fields).Dup(false)
}

func (p *Point) MarshalJSON() ([]byte, error) {
	p.m.RLock()
	defer p.m.RUnlock()
	jsonPoint := struct {
		Key       string
		Timestamp int64 `json:",string"`
		Tags      jsonFields
		Fields    jsonFields
	}{
		p.key,
		clock.Now().UnixNano(),
		makeJSONFields(p.fields, p.fieldOrder),
		makeJSONTags(p.tags, p.tagOrder),
	}

	return json.Marshal(jsonPoint)
}
<commit_msg>Remove unused dupFields method<commit_after>package dagr

import (
	"encoding/json"
	"io"
	"sort"
	"sync"
	"time"
)

type Tags map[string]string

// Dup clones the Tags map, t. If t is nil, it returns nil.
func (t Tags) Dup() Tags {
	if t == nil {
		return nil
	}

	d := make(Tags, len(t))
	for name, tag := range t {
		d[name] = tag
	}
	return d
}

type Fields map[string]Field

// Dup clones the Fields map. If deep is true, it will also duplicate the fields held, creating new field instances,
// otherwise it retains its references to the Fields held by fs. If fs is nil, it returns nil.
func (fs Fields) Dup(deep bool) Fields {
	if fs == nil {
		return nil
	}

	d := make(Fields, len(fs))
	for name, field := range fs {
		if deep {
			field = field.Dup()
		}
		d[name] = field
	}
	return d
}

// Measurement defines the minimum interface for a measurement that could be passed to InfluxDB. All measurements must
// have a key and a minimum of one field. Tags are optional.
//
// Unless stated otherwise, the results of Tags and Fields are considered immutable.
type Measurement interface {
	Key() string
	Tags() Tags
	Fields() Fields
}

// TimeMeasurement is a measurement that has a known, known, fixed time. It can be considered a measurement that is
// fixed to a specific point in time. TimeMeasurements are not necessarily read-only and may, for example, return
// time.Now(). In general, it is better to not implement TimeMeasurement if your measurement is returning time.Now().
type TimeMeasurement interface {
	Time() time.Time

	Measurement
}

type fixedField struct {
	prefix []byte
	fields []fixedField
}

// Point is a single Measurement as understood by InfluxDB.
type Point struct {
	key        string
	tagOrder   []string
	fieldOrder []string
	tags       map[string]string
	fields     map[string]Field
	m          sync.RWMutex
}

var _ = Measurement((*Point)(nil))

// NewPoint allocates a new Point with the given key, tags, and fields. If key is empty, NewPoint panics. If fields is
// empty, the point cannot be written until it has at least one field.
func NewPoint(key string, tags Tags, fields Fields) *Point {
	if key == "" {
		panic("dagr.NewPoint: key is empty")
	}

	p := &Point{
		key:    key,
		tags:   make(map[string]string, len(tags)),
		fields: make(map[string]Field, len(fields)),
	}

	for name, tag := range tags {
		p.addTag(name, tag)
	}

	for name, field := range fields {
		p.addField(name, field)
	}

	return p
}

// WriteTo writes the point to the given writer, w. If an error occurs while building the point, it writes nothing and
// return an error. If the point has no fields, it returns the error ErrNoFields.
func (p *Point) WriteTo(w io.Writer) (int64, error) {
	buf := getBuffer(w)
	p.m.RLock()
	defer func() {
		p.m.RUnlock()
		putBuffer(buf)
	}()

	buf.WriteString(tagEscaper.Replace(p.key))
	writeTags(buf, p.tags, p.tagOrder)

	buf.WriteByte(' ')
	if err := writeFields(buf, p.fields, p.fieldOrder); err != nil {
		buf.Truncate(int(buf.head))
		return 0, err
	}

	buf.WriteByte(' ')
	writeTimestamp(buf, clock.Now())
	buf.WriteByte('\n')

	return buf.WriteTo(w)
}

// Key returns the point's key.
func (p *Point) Key() string {
	return p.key
}

// Compiled returns a compiled form of the point. The resulting Measurement is immutable except for its field values.
// New fields may not be added and it will not return a key, tags, or values. The compiled form of a point is only
// useful to improve write times on points when necessary. If the point has no fields, it returns nil, as the point is
// not valid to write.
func (p *Point) Compiled() Measurement {
	p.m.RLock()
	p.m.RUnlock()

	if len(p.fieldOrder) == 0 {
		return nil
	}
	return p.compile()
}

func (p *Point) compile() compiledPoint {
	var c compiledPoint
	buf := getBuffer(nil)
	defer putBuffer(buf)

	// Write key
	buf.WriteString(tagEscaper.Replace(p.key))
	// Write tags
	writeTags(buf, p.tags, p.tagOrder)
	c.lead = buf.Len()
	// Write field names
	var pre byte = ' '
	fields := make([]compiledField, len(p.fieldOrder))
	for i, name := range p.fieldOrder {
		field := p.fields[name]

		from := buf.Len()
		buf.WriteByte(pre)
		pre = ','

		buf.WriteString(tagEscaper.Replace(name))
		buf.WriteByte('=')

		to := buf.Len()
		if i == 0 {
			from = to
			c.lead = to
		}
		fields[i] = compiledField{from, to, field}
	}

	c.prefix = append([]byte(nil), buf.Bytes()...)
	c.fields = fields

	return c
}

// Tags

func (p *Point) addTag(name, value string) {
	_, exists := p.tags[name]
	p.tags[name] = value
	if exists {
		return
	}
	p.tagOrder = insertOrderedString(p.tagOrder, name)
}

// SetTag sets a tag on the point with the given name and value. If the value is empty, the tag is removed. If the name
// is empty, the call is a no-op. It is safe to call SetTag from concurrent goroutines.
func (p *Point) SetTag(name, value string) {
	if name == "" {
		return
	} else if value == "" {
		p.RemoveTag(name)
		return
	}

	p.m.Lock()
	defer p.m.Unlock()
	p.addTag(name, value)
}

// RemoveTag removes the tag with the given name from the point. If name is empty, the call is a no-op. It is safe to
// call from concurrent goroutines.
func (p *Point) RemoveTag(name string) {
	if name == "" {
		return
	}

	p.m.Lock()
	defer p.m.Unlock()

	if _, ok := p.tags[name]; !ok {
		// No tag
		return
	}

	delete(p.tags, name)
	for i, tname := range p.tagOrder {
		if name == tname {
			copy(p.tagOrder[i:], p.tagOrder[i+1:])
			p.tagOrder = p.tagOrder[:len(p.tagOrder)-1]
			break
		}
	}
}

// Tags returns a copy of the point's tags as a map of names to values. Names and values are not escaped. It is safe to
// copy and modify the result of this method.
func (p *Point) Tags() Tags {
	p.m.RLock()
	defer p.m.RUnlock()
	return Tags(p.tags).Dup()
}

// Fields

// insertOrderedString inserts str in the ordered slice of strings. It assumes that all elements of slice are unique,
// though this isn't necessarily important except that having non-unique names would result in Point having a memory
// leak.
func insertOrderedString(slice []string, str string) []string {
	n := len(slice)
	if idx := sort.SearchStrings(slice, str); idx < n {
		slice = append(slice, "")
		copy(slice[idx+1:], slice[idx:n])
		slice[idx] = str
	} else {
		slice = append(slice, str)
	}
	return slice
}

func (p *Point) addField(name string, value Field) {
	_, exists := p.fields[name]
	p.fields[name] = value
	if exists {
		return
	}
	p.fieldOrder = insertOrderedString(p.fieldOrder, name)
}

// SetField sets a field with the given name and value. If name is empty, the call is a no-op. If value is nil, it
// removes the field.
func (p *Point) SetField(name string, value Field) {
	if name == "" {
		return
	} else if value == nil {
		p.RemoveField(name)
		return
	}

	p.m.Lock()
	defer p.m.Unlock()
	p.addField(name, value)
}

// RemoveField removes a field with the given name. If name is empty, the call is a no-op. It is safe to call
// RemoveField from concurrent goroutines.
func (p *Point) RemoveField(name string) {
	if name == "" {
		return
	}

	p.m.Lock()
	defer p.m.Unlock()

	if _, ok := p.fields[name]; !ok {
		// No field
		return
	}

	delete(p.fields, name)
	for i, fname := range p.fieldOrder {
		if name == fname {
			copy(p.fieldOrder[i:], p.fieldOrder[i+1:])
			p.fieldOrder = p.fieldOrder[:len(p.fieldOrder)-1]
			break
		}
	}
}

// Fields returns a map of the point's fields. This map is a copy of the point's state and may be modified without
// affecting the point. The fields themselves are those held by the point, however, and modifying them will modify the
// point's state.
func (p *Point) Fields() Fields {
	p.m.RLock()
	defer p.m.RUnlock()
	return Fields(p.fields).Dup(false)
}

func (p *Point) MarshalJSON() ([]byte, error) {
	p.m.RLock()
	defer p.m.RUnlock()
	jsonPoint := struct {
		Key       string
		Timestamp int64 `json:",string"`
		Tags      jsonFields
		Fields    jsonFields
	}{
		p.key,
		clock.Now().UnixNano(),
		makeJSONFields(p.fields, p.fieldOrder),
		makeJSONTags(p.tags, p.tagOrder),
	}

	return json.Marshal(jsonPoint)
}
<|endoftext|>


<commit_before>package main

import (
	proto "github.com/huin/mqtt"
	"log"
	"sync"
)

type MemStorage struct {
	clientsMu  sync.Mutex
	clients    map[string]*Connection // clientid -> list of Connection
	TopicTable map[string][]string    // topic -> list of clientid
	RetainMap  map[string]*proto.Publish
}

func (mem *MemStorage) MergeClient(clientid string, conn *Connection) (*Connection, error) {
	mem.clientsMu.Lock()
	defer mem.clientsMu.Unlock()

	if _, ok := mem.clients[clientid]; ok {
		c := mem.clients[clientid]
		if c.Status == ClientAvailable {
			log.Println("Re-con")
		}

		return c, nil
	}
	mem.clients[clientid] = conn

	return conn, nil
}

func (mem *MemStorage) DeleteClient(clientid string, conn *Connection) error {
	return nil
}

func (mem *MemStorage) AddMsg(m *proto.Publish) {

}

func (mem *MemStorage) DeleteMsg(m *proto.Publish) {

}

func (mem *MemStorage) Flush() {

}

func (mem *MemStorage) GetTopicClientList(topic string) []string {
	return mem.TopicTable[topic]
}

func (mem *MemStorage) GetClientConnection(topic string) *Connection {
	return mem.clients[topic]
}

func (mem *MemStorage) Subscribe(topic string, clientid string) {
	mem.TopicTable[topic] = append(mem.TopicTable[topic], clientid)
}

func (mem *MemStorage) Unsubscribe(topic string, clientid string) {
	// TODO
}

func (mem *MemStorage) UpdateRetain(topic string, m *proto.Publish) {
	//does not need lock or check exists. just update it
	mem.RetainMap[topic] = m
}
func (mem *MemStorage) GetRetain(topic string) (*proto.Publish, bool) {
	m, ok := mem.RetainMap[topic]
	return m, ok
}

func NewMemStorage() *MemStorage {
	s := &MemStorage{
		clients:    make(map[string]*Connection),
		TopicTable: make(map[string][]string),
		RetainMap:  make(map[string]*proto.Publish),
	}
	return s
}
<commit_msg>fix the arg name.<commit_after>package main

import (
	proto "github.com/huin/mqtt"
	"log"
	"sync"
)

type MemStorage struct {
	clientsMu  sync.Mutex
	clients    map[string]*Connection // clientid -> list of Connection
	TopicTable map[string][]string    // topic -> list of clientid
	RetainMap  map[string]*proto.Publish
}

func (mem *MemStorage) MergeClient(clientid string, conn *Connection) (*Connection, error) {
	mem.clientsMu.Lock()
	defer mem.clientsMu.Unlock()

	if _, ok := mem.clients[clientid]; ok {
		c := mem.clients[clientid]
		if c.Status == ClientAvailable {
			log.Println("Re-con")
		}

		return c, nil
	}
	mem.clients[clientid] = conn

	return conn, nil
}

func (mem *MemStorage) DeleteClient(clientid string, conn *Connection) error {
	return nil
}

func (mem *MemStorage) AddMsg(m *proto.Publish) {

}

func (mem *MemStorage) DeleteMsg(m *proto.Publish) {

}

func (mem *MemStorage) Flush() {

}

func (mem *MemStorage) GetTopicClientList(topic string) []string {
	return mem.TopicTable[topic]
}

func (mem *MemStorage) GetClientConnection(clientid string) *Connection {
	return mem.clients[clientid]
}

func (mem *MemStorage) Subscribe(topic string, clientid string) {
	mem.TopicTable[topic] = append(mem.TopicTable[topic], clientid)
}

func (mem *MemStorage) Unsubscribe(topic string, clientid string) {
	// TODO
}

func (mem *MemStorage) UpdateRetain(topic string, m *proto.Publish) {
	//does not need lock or check exists. just update it
	mem.RetainMap[topic] = m
}
func (mem *MemStorage) GetRetain(topic string) (*proto.Publish, bool) {
	m, ok := mem.RetainMap[topic]
	return m, ok
}

func NewMemStorage() *MemStorage {
	s := &MemStorage{
		clients:    make(map[string]*Connection),
		TopicTable: make(map[string][]string),
		RetainMap:  make(map[string]*proto.Publish),
	}
	return s
}
<|endoftext|>


<commit_before>package configuration

import (
	"flag"
	"log"
	"os"

	"github.com/FogCreek/mini"
)

type iagoConfiguration struct {
	Hostname string
	Protocol string
	Port     int64
	Path     string
}

type checkinConfiguration struct {
	Hostname string
	Protocol string
	Port     int64
	Path     string
	TTL      int64
}

type webConfiguration struct {
	Address string
}

type notificationConfiguration struct {
	Signed    bool
	PublicKey string
}

var (
	Iago         iagoConfiguration
	CheckIn      checkinConfiguration
	Web          webConfiguration
	Notification notificationConfiguration
)

func Process() {
	path := flag.String("config", "/etc/miloud.ini", "Configuration file path")
	flag.Parse()

	config, err := mini.LoadConfiguration(*path)

	if err != nil {
		log.Fatal(err)
	}

	Iago.Hostname = config.StringFromSection("Iago", "Hostname", "localhost")
	Iago.Protocol = config.StringFromSection("Iago", "Protocol", "http")
	Iago.Path = config.StringFromSection("Iago", "Path", "/")
	Iago.Port = config.IntegerFromSection("Iago", "Port", 0)

	if Iago.Port == 0 {
		if Iago.Protocol == "http" {
			Iago.Port = 80
		} else if Iago.Protocol == "https" {
			Iago.Port = 443
		}
	}

	CheckIn.Hostname = config.StringFromSection("CheckIn", "Hostname", "")
	CheckIn.Protocol = config.StringFromSection("CheckIn", "Protocol", "http")
	CheckIn.Path = config.StringFromSection("CheckIn", "Path", "/")
	CheckIn.Port = config.IntegerFromSection("CheckIn", "Port", 0)
	CheckIn.TTL = config.IntegerFromSection("CheckIn", "TTL", 30)

	if CheckIn.Hostname == "" {
		hostname, err := os.Hostname()

		if err != nil {
			log.Fatal(err)
		}

		CheckIn.Hostname = hostname
	}

	if CheckIn.Port == 0 {
		if CheckIn.Protocol == "http" {
			CheckIn.Port = 80
		} else if CheckIn.Protocol == "https" {
			CheckIn.Port = 443
		}
	}

	Web.Address = config.StringFromSection("Web", "Address", ":9090")

	Notification.Signed = config.BooleanFromSection("Notification", "Signed", false)
	Notification.PublicKey = config.StringFromSection("Notification", "PublicKey", "/etc/iagod/key.pub")
}
<commit_msg>Implemented parsing the project list and project sections<commit_after>package configuration

import (
	"bytes"
	"flag"
	"log"
	"os"

	"github.com/FogCreek/mini"
	"github.com/citruspi/Milou/projects"
)

type iagoConfiguration struct {
	Hostname string
	Protocol string
	Port     int64
	Path     string
}

type checkinConfiguration struct {
	Hostname string
	Protocol string
	Port     int64
	Path     string
	TTL      int64
}

type webConfiguration struct {
	Address string
}

type notificationConfiguration struct {
	Signed    bool
	PublicKey string
}

var (
	Iago         iagoConfiguration
	CheckIn      checkinConfiguration
	Web          webConfiguration
	Notification notificationConfiguration
)

func Process() {
	path := flag.String("config", "/etc/miloud.ini", "Configuration file path")
	flag.Parse()

	config, err := mini.LoadConfiguration(*path)

	if err != nil {
		log.Fatal(err)
	}

	Iago.Hostname = config.StringFromSection("Iago", "Hostname", "localhost")
	Iago.Protocol = config.StringFromSection("Iago", "Protocol", "http")
	Iago.Path = config.StringFromSection("Iago", "Path", "/")
	Iago.Port = config.IntegerFromSection("Iago", "Port", 0)

	if Iago.Port == 0 {
		if Iago.Protocol == "http" {
			Iago.Port = 80
		} else if Iago.Protocol == "https" {
			Iago.Port = 443
		}
	}

	CheckIn.Hostname = config.StringFromSection("CheckIn", "Hostname", "")
	CheckIn.Protocol = config.StringFromSection("CheckIn", "Protocol", "http")
	CheckIn.Path = config.StringFromSection("CheckIn", "Path", "/")
	CheckIn.Port = config.IntegerFromSection("CheckIn", "Port", 0)
	CheckIn.TTL = config.IntegerFromSection("CheckIn", "TTL", 30)

	if CheckIn.Hostname == "" {
		hostname, err := os.Hostname()

		if err != nil {
			log.Fatal(err)
		}

		CheckIn.Hostname = hostname
	}

	if CheckIn.Port == 0 {
		if CheckIn.Protocol == "http" {
			CheckIn.Port = 80
		} else if CheckIn.Protocol == "https" {
			CheckIn.Port = 443
		}
	}

	Web.Address = config.StringFromSection("Web", "Address", ":9090")

	Notification.Signed = config.BooleanFromSection("Notification", "Signed", false)
	Notification.PublicKey = config.StringFromSection("Notification", "PublicKey", "/etc/iagod/key.pub")

	projectList := config.StringsFromSection("Milou", "Projects")

	for _, projectName := range projectList {
		project := projects.Project{}

		var buffer bytes.Buffer
		buffer.WriteString("Project-")
		buffer.WriteString(projectName)

		section := string(buffer.Bytes())

		project.Name = config.StringFromSection(section, "Name", "")
		project.Owner = config.StringFromSection(section, "Owner", "")
		project.Repository = config.StringFromSection(section, "Repository", "")
		project.Version = config.StringFromSection(section, "Version", "")
		project.Identifier = config.StringFromSection(section, "Identifier", "")
		project.Domain = config.StringFromSection(section, "Domain", "")
		project.Subdomain = config.StringFromSection(section, "Subdomain", "")
		project.Type = config.StringFromSection(section, "Type", "")

		projects.List = append(projects.List, project)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2022 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package http

import (
	"fmt"
	"net/url"
	"strings"
	"testing"
	"time"

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

	"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
)

// ExpectedResponse defines the response expected for a given request.
type ExpectedResponse struct {
	Request    ExpectedRequest
	StatusCode int
	Backend    string
	Namespace  string
}

// ExpectedRequest can be used as both the request to make and a means to verify
// that echoserver received the expected request.
type ExpectedRequest struct {
	Host    string
	Method  string
	Path    string
	Headers map[string]string
}

const maxConsistencyPeriodPerRequest = 60 * time.Second
const numConsistencyChecksPerRequest = 3

// MakeRequestAndExpectEventuallyConsistentResponse makes a request with the given parameters,
// understanding that the request may fail for some amount of time.
//
// Once the request succeeds and the response has the expected status code, assert that
// a response matching the provided ExpectedResponse is returned consistently.
func MakeRequestAndExpectEventuallyConsistentResponse(t *testing.T, r roundtripper.RoundTripper, gwAddr string, expected ExpectedResponse) {
	t.Helper()

	if expected.Request.Method == "" {
		expected.Request.Method = "GET"
	}

	if expected.StatusCode == 0 {
		expected.StatusCode = 200
	}

	t.Logf("Making %s request to http://%s%s", expected.Request.Method, gwAddr, expected.Request.Path)

	req := roundtripper.Request{
		Method:   expected.Request.Method,
		Host:     expected.Request.Host,
		URL:      url.URL{Scheme: "http", Host: gwAddr, Path: expected.Request.Path},
		Protocol: "HTTP",
	}

	if expected.Request.Headers != nil {
		req.Headers = map[string][]string{}
		for name, value := range expected.Request.Headers {
			req.Headers[name] = []string{value}
		}
	}

	// We expect eventual consistency once the resources are ready, so wait until
	// we've made a successful request and gotten a response with desired status
	// before making assertions on the response body.
	require.Eventually(t, func() bool {
		var err error

		_, cRes, err := r.CaptureRoundTrip(req)
		if err != nil {
			t.Logf("Request failed, not ready yet: %v", err.Error())
			return false
		}

		if cRes.StatusCode != expected.StatusCode {
			t.Logf("Expected response to have status %d but got %d, not ready yet", expected.StatusCode, cRes.StatusCode)
			return false
		}

		return true
	}, maxConsistencyPeriodPerRequest, 1*time.Second, "error making request, never got expected status")

	// Once we've made a successful request and gotten a response with the
	// desired status, assert that we get the expected response consistently.
	for i := 1; i <= numConsistencyChecksPerRequest; i++ {
		t.Run(fmt.Sprintf("consistency_check_%d", i), func(t *testing.T) {
			cReq, cRes, err := r.CaptureRoundTrip(req)
			require.NoError(t, err, "error making request after previous success")

			ExpectResponse(t, cReq, cRes, expected)
		})
	}
}

// ExpectResponse verifies that a captured request and response match the
// provided ExpectedResponse.
func ExpectResponse(t *testing.T, cReq *roundtripper.CapturedRequest, cRes *roundtripper.CapturedResponse, expected ExpectedResponse) {
	t.Helper()
	assert.Equal(t, expected.StatusCode, cRes.StatusCode, "expected status code to be %d, got %d", expected.StatusCode, cRes.StatusCode)
	if cRes.StatusCode == 200 {
		assert.Equal(t, expected.Request.Path, cReq.Path, "expected path to be %s, got %s", expected.Request.Path, cReq.Path)
		assert.Equal(t, expected.Request.Method, cReq.Method, "expected method to be %s, got %s", expected.Request.Method, cReq.Method)
		assert.Equal(t, expected.Namespace, cReq.Namespace, "expected namespace to be %s, got %s", expected.Namespace, cReq.Namespace)
		if expected.Request.Headers != nil {
			if cReq.Headers == nil {
				t.Error("No headers captured")
			} else {
				for name, val := range cReq.Headers {
					cReq.Headers[strings.ToLower(name)] = val
				}
				for name, expectedVal := range expected.Request.Headers {
					actualVal, ok := cReq.Headers[strings.ToLower(name)]
					if !ok {
						t.Errorf("Expected %s header to be set, actual headers: %v", name, cReq.Headers)
					} else if actualVal[0] != expectedVal {
						t.Errorf("Expected %s header to be set to %s, got %s", name, expectedVal, actualVal[0])
					}
				}
			}
		}
		if !strings.HasPrefix(cReq.Pod, expected.Backend) {
			t.Errorf("Expected pod name to start with %s, got %s", expected.Backend, cReq.Pod)
		}
	}
}
<commit_msg>Allow for intermittent success prior to reaching consistency<commit_after>/*
Copyright 2022 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package http

import (
	"net/url"
	"strings"
	"testing"
	"time"

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

	"sigs.k8s.io/gateway-api/conformance/utils/roundtripper"
)

// ExpectedResponse defines the response expected for a given request.
type ExpectedResponse struct {
	Request    ExpectedRequest
	StatusCode int
	Backend    string
	Namespace  string
}

// ExpectedRequest can be used as both the request to make and a means to verify
// that echoserver received the expected request.
type ExpectedRequest struct {
	Host    string
	Method  string
	Path    string
	Headers map[string]string
}

const maxConsistencyPeriodPerRequest = 60 * time.Second
const numConsistencyChecksPerRequest = 3

// MakeRequestAndExpectEventuallyConsistentResponse makes a request with the given parameters,
// understanding that the request may fail for some amount of time.
//
// Once the request succeeds consistently with the response having the expected status code, make
// additional assertions on the response body using the provided ExpectedResponse.
func MakeRequestAndExpectEventuallyConsistentResponse(t *testing.T, r roundtripper.RoundTripper, gwAddr string, expected ExpectedResponse) {
	t.Helper()

	if expected.Request.Method == "" {
		expected.Request.Method = "GET"
	}

	if expected.StatusCode == 0 {
		expected.StatusCode = 200
	}

	t.Logf("Making %s request to http://%s%s", expected.Request.Method, gwAddr, expected.Request.Path)

	req := roundtripper.Request{
		Method:   expected.Request.Method,
		Host:     expected.Request.Host,
		URL:      url.URL{Scheme: "http", Host: gwAddr, Path: expected.Request.Path},
		Protocol: "HTTP",
	}

	if expected.Request.Headers != nil {
		req.Headers = map[string][]string{}
		for name, value := range expected.Request.Headers {
			req.Headers[name] = []string{value}
		}
	}

	cReq, cRes := WaitForConsistency(t, r, req, expected, numConsistencyChecksPerRequest)
	ExpectResponse(t, cReq, cRes, expected)
}

// WaitForConsistency repeats the provided request until it completes with a response having
// the expected status code consistently. The provided threshold determines how many times in
// a row this must occur to be considered "consistent".
func WaitForConsistency(t *testing.T, r roundtripper.RoundTripper, req roundtripper.Request, expected ExpectedResponse, threshold int) (*roundtripper.CapturedRequest, *roundtripper.CapturedResponse) {
	var (
		cReq         *roundtripper.CapturedRequest
		cRes         *roundtripper.CapturedResponse
		err          error
		numSuccesses int
	)

	require.Eventually(t, func() bool {
		cReq, cRes, err = r.CaptureRoundTrip(req)
		if err != nil {
			numSuccesses = 0
			t.Logf("Request failed, not ready yet: %v", err.Error())
			return false
		}

		if cRes.StatusCode != expected.StatusCode {
			numSuccesses = 0
			t.Logf("Expected response to have status %d but got %d, not ready yet", expected.StatusCode, cRes.StatusCode)
			return false
		}

		numSuccesses++
		if numSuccesses < threshold {
			t.Logf("Request has passed %d times in a row of the desired %d, not ready yet", numSuccesses, threshold)
			return false
		}

		return true
	}, maxConsistencyPeriodPerRequest, 1*time.Second, "error making request, never got expected status")

	return cReq, cRes
}

// ExpectResponse verifies that a captured request and response match the
// provided ExpectedResponse.
func ExpectResponse(t *testing.T, cReq *roundtripper.CapturedRequest, cRes *roundtripper.CapturedResponse, expected ExpectedResponse) {
	t.Helper()
	assert.Equal(t, expected.StatusCode, cRes.StatusCode, "expected status code to be %d, got %d", expected.StatusCode, cRes.StatusCode)
	if cRes.StatusCode == 200 {
		assert.Equal(t, expected.Request.Path, cReq.Path, "expected path to be %s, got %s", expected.Request.Path, cReq.Path)
		assert.Equal(t, expected.Request.Method, cReq.Method, "expected method to be %s, got %s", expected.Request.Method, cReq.Method)
		assert.Equal(t, expected.Namespace, cReq.Namespace, "expected namespace to be %s, got %s", expected.Namespace, cReq.Namespace)
		if expected.Request.Headers != nil {
			if cReq.Headers == nil {
				t.Error("No headers captured")
			} else {
				for name, val := range cReq.Headers {
					cReq.Headers[strings.ToLower(name)] = val
				}
				for name, expectedVal := range expected.Request.Headers {
					actualVal, ok := cReq.Headers[strings.ToLower(name)]
					if !ok {
						t.Errorf("Expected %s header to be set, actual headers: %v", name, cReq.Headers)
					} else if actualVal[0] != expectedVal {
						t.Errorf("Expected %s header to be set to %s, got %s", name, expectedVal, actualVal[0])
					}
				}
			}
		}
		if !strings.HasPrefix(cReq.Pod, expected.Backend) {
			t.Errorf("Expected pod name to start with %s, got %s", expected.Backend, cReq.Pod)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package constraints_test

import (
	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/constraints"
)

type validationSuite struct{}

var _ = gc.Suite(&validationSuite{})

var validationTests = []struct {
	cons        string
	unsupported []string
	vocab       map[string][]interface{}
	reds        []string
	blues       []string
	err         string
}{
	{
		cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
	},
	{
		cons:        "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo",
		unsupported: []string{"tags"},
	},
	{
		cons:        "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 instance-type=foo",
		unsupported: []string{"cpu-power", "instance-type"},
	},
	{
		// Ambiguous constraint errors take precedence over unsupported errors.
		cons:        "root-disk=8G mem=4G cpu-cores=4 instance-type=foo",
		reds:        []string{"mem", "arch"},
		blues:       []string{"instance-type"},
		unsupported: []string{"cpu-cores"},
		err:         `ambiguous constraints: "mem" overlaps with "instance-type"`,
	},
	{
		cons: "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		reds: []string{"mem", "arch"},
		err:  "",
	},
	{
		cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		blues: []string{"mem", "arch"},
		err:   "",
	},
	{
		cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		reds:  []string{"mem", "arch"},
		blues: []string{"instance-type"},
		err:   `ambiguous constraints: "arch" overlaps with "instance-type"`,
	},
	{
		cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		reds:  []string{"instance-type"},
		blues: []string{"mem", "arch"},
		err:   `ambiguous constraints: "arch" overlaps with "instance-type"`,
	},
	{
		cons:  "root-disk=8G mem=4G cpu-cores=4 instance-type=foo",
		reds:  []string{"mem", "arch"},
		blues: []string{"instance-type"},
		err:   `ambiguous constraints: "mem" overlaps with "instance-type"`,
	},
	{
		cons:  "arch=amd64 mem=4G cpu-cores=4",
		vocab: map[string][]interface{}{"arch": []interface{}{"amd64", "i386"}},
	},
	{
		cons:  "mem=4G cpu-cores=4",
		vocab: map[string][]interface{}{"cpu-cores": []interface{}{2, 4, 8}},
	},
	{
		cons:  "mem=4G instance-type=foo",
		vocab: map[string][]interface{}{"instance-type": []interface{}{"foo", "bar"}},
	},
	{
		cons:  "mem=4G tags=foo,bar",
		vocab: map[string][]interface{}{"tags": []interface{}{"foo", "bar", "another"}},
	},
	{
		cons:  "arch=i386 mem=4G cpu-cores=4",
		vocab: map[string][]interface{}{"arch": []interface{}{"amd64"}},
		err:   "invalid constraint value: arch=i386\nvalid values are:.*",
	},
	{
		cons:  "mem=4G cpu-cores=5",
		vocab: map[string][]interface{}{"cpu-cores": []interface{}{2, 4, 8}},
		err:   "invalid constraint value: cpu-cores=5\nvalid values are:.*",
	},
	{
		cons:  "mem=4G instance-type=foo",
		vocab: map[string][]interface{}{"instance-type": []interface{}{"bar"}},
		err:   "invalid constraint value: instance-type=foo\nvalid values are:.*",
	},
	{
		cons:  "mem=4G tags=foo,other",
		vocab: map[string][]interface{}{"tags": []interface{}{"foo", "bar", "another"}},
		err:   "invalid constraint value: tags=other\nvalid values are:.*",
	},
	{
		cons: "arch=i386 mem=4G instance-type=foo",
		vocab: map[string][]interface{}{
			"instance-type": []interface{}{"foo", "bar"},
			"arch":          []interface{}{"amd64", "i386"}},
	},
}

func (s *validationSuite) TestValidation(c *gc.C) {
	for i, t := range validationTests {
		c.Logf("test %d", i)
		validator := constraints.NewValidator()
		validator.RegisterUnsupported(t.unsupported)
		validator.RegisterConflicts(t.reds, t.blues)
		for a, v := range t.vocab {
			validator.RegisterVocabulary(a, v)
		}
		cons := constraints.MustParse(t.cons)
		unsupported, err := validator.Validate(cons)
		if t.err == "" {
			c.Assert(err, gc.IsNil)
			c.Assert(unsupported, jc.DeepEquals, t.unsupported)
		} else {
			c.Assert(err, gc.ErrorMatches, t.err)
		}
	}
}

var mergeTests = []struct {
	desc         string
	consFallback string
	cons         string
	unsupported  []string
	reds         []string
	blues        []string
	expected     string
}{
	{
		desc: "empty all round",
	}, {
		desc:     "container with empty fallback",
		cons:     "container=lxc",
		expected: "container=lxc",
	}, {
		desc:         "container from fallback",
		consFallback: "container=lxc",
		expected:     "container=lxc",
	}, {
		desc:     "arch with empty fallback",
		cons:     "arch=amd64",
		expected: "arch=amd64",
	}, {
		desc:         "arch with ignored fallback",
		cons:         "arch=amd64",
		consFallback: "arch=i386",
		expected:     "arch=amd64",
	}, {
		desc:         "arch from fallback",
		consFallback: "arch=i386",
		expected:     "arch=i386",
	}, {
		desc:     "instance type with empty fallback",
		cons:     "instance-type=foo",
		expected: "instance-type=foo",
	}, {
		desc:         "instance type with ignored fallback",
		cons:         "instance-type=foo",
		consFallback: "instance-type=bar",
		expected:     "instance-type=foo",
	}, {
		desc:         "instance type from fallback",
		consFallback: "instance-type=foo",
		expected:     "instance-type=foo",
	}, {
		desc:     "cpu-cores with empty fallback",
		cons:     "cpu-cores=2",
		expected: "cpu-cores=2",
	}, {
		desc:         "cpu-cores with ignored fallback",
		cons:         "cpu-cores=4",
		consFallback: "cpu-cores=8",
		expected:     "cpu-cores=4",
	}, {
		desc:         "cpu-cores from fallback",
		consFallback: "cpu-cores=8",
		expected:     "cpu-cores=8",
	}, {
		desc:     "cpu-power with empty fallback",
		cons:     "cpu-power=100",
		expected: "cpu-power=100",
	}, {
		desc:         "cpu-power with ignored fallback",
		cons:         "cpu-power=100",
		consFallback: "cpu-power=200",
		expected:     "cpu-power=100",
	}, {
		desc:         "cpu-power from fallback",
		consFallback: "cpu-power=200",
		expected:     "cpu-power=200",
	}, {
		desc:     "tags with empty fallback",
		cons:     "tags=foo,bar",
		expected: "tags=foo,bar",
	}, {
		desc:         "tags with ignored fallback",
		cons:         "tags=foo,bar",
		consFallback: "tags=baz",
		expected:     "tags=foo,bar",
	}, {
		desc:         "tags from fallback",
		consFallback: "tags=foo,bar",
		expected:     "tags=foo,bar",
	}, {
		desc:         "tags inital empty",
		cons:         "tags=",
		consFallback: "tags=foo,bar",
		expected:     "tags=",
	}, {
		desc:     "mem with empty fallback",
		cons:     "mem=4G",
		expected: "mem=4G",
	}, {
		desc:         "mem with ignored fallback",
		cons:         "mem=4G",
		consFallback: "mem=8G",
		expected:     "mem=4G",
	}, {
		desc:         "mem from fallback",
		consFallback: "mem=8G",
		expected:     "mem=8G",
	}, {
		desc:     "root-disk with empty fallback",
		cons:     "root-disk=4G",
		expected: "root-disk=4G",
	}, {
		desc:         "root-disk with ignored fallback",
		cons:         "root-disk=4G",
		consFallback: "root-disk=8G",
		expected:     "root-disk=4G",
	}, {
		desc:         "root-disk from fallback",
		consFallback: "root-disk=8G",
		expected:     "root-disk=8G",
	}, {
		desc:         "non-overlapping mix",
		cons:         "root-disk=8G mem=4G arch=amd64",
		consFallback: "cpu-power=1000 cpu-cores=4",
		expected:     "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
	}, {
		desc:         "overlapping mix",
		cons:         "root-disk=8G mem=4G arch=amd64",
		consFallback: "cpu-power=1000 cpu-cores=4 mem=8G",
		expected:     "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
	}, {
		desc:         "fallback only, no conflicts",
		consFallback: "root-disk=8G cpu-cores=4 instance-type=foo",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=foo",
	}, {
		desc:     "no fallback, no conflicts",
		cons:     "root-disk=8G cpu-cores=4 instance-type=foo",
		reds:     []string{"mem", "arch"},
		blues:    []string{"instance-type"},
		expected: "root-disk=8G cpu-cores=4 instance-type=foo",
	}, {
		desc:         "conflict value from override",
		consFallback: "root-disk=8G instance-type=foo",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "unsupported attributes ignored",
		consFallback: "root-disk=8G instance-type=foo",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		unsupported:  []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "red conflict masked from fallback",
		consFallback: "root-disk=8G mem=4G",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "second red conflict masked from fallback",
		consFallback: "root-disk=8G arch=amd64",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "blue conflict masked from fallback",
		consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
		cons:         "root-disk=8G mem=4G",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 mem=4G",
	}, {
		desc:         "both red conflicts used, blue mased from fallback",
		consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
		cons:         "root-disk=8G arch=amd64 mem=4G",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 arch=amd64 mem=4G",
	},
}

func (s *validationSuite) TestMerge(c *gc.C) {
	for i, t := range mergeTests {
		c.Logf("test %d: %s", i, t.desc)
		validator := constraints.NewValidator()
		validator.RegisterConflicts(t.reds, t.blues)
		consFallback := constraints.MustParse(t.consFallback)
		cons := constraints.MustParse(t.cons)
		merged, err := validator.Merge(consFallback, cons)
		c.Assert(err, gc.IsNil)
		expected := constraints.MustParse(t.expected)
		c.Check(merged, gc.DeepEquals, expected)
	}
}

func (s *validationSuite) TestMergeError(c *gc.C) {
	validator := constraints.NewValidator()
	validator.RegisterConflicts([]string{"instance-type"}, []string{"mem"})
	consFallback := constraints.MustParse("instance-type=foo mem=4G")
	cons := constraints.MustParse("cpu-cores=2")
	_, err := validator.Merge(consFallback, cons)
	c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "mem" overlaps with "instance-type"`)
	_, err = validator.Merge(cons, consFallback)
	c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "mem" overlaps with "instance-type"`)
}
<commit_msg>[r=fwereade],[bug=1320138] Fix LP 1320138<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package constraints_test

import (
	jc "github.com/juju/testing/checkers"
	gc "launchpad.net/gocheck"

	"launchpad.net/juju-core/constraints"
)

type validationSuite struct{}

var _ = gc.Suite(&validationSuite{})

var validationTests = []struct {
	cons        string
	unsupported []string
	vocab       map[string][]interface{}
	reds        []string
	blues       []string
	err         string
}{
	{
		cons: "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
	},
	{
		cons:        "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 tags=foo",
		unsupported: []string{"tags"},
	},
	{
		cons:        "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4 instance-type=foo",
		unsupported: []string{"cpu-power", "instance-type"},
	},
	{
		// Ambiguous constraint errors take precedence over unsupported errors.
		cons:        "root-disk=8G mem=4G cpu-cores=4 instance-type=foo",
		reds:        []string{"mem", "arch"},
		blues:       []string{"instance-type"},
		unsupported: []string{"cpu-cores"},
		err:         `ambiguous constraints: "mem" overlaps with "instance-type"`,
	},
	{
		cons: "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		reds: []string{"mem", "arch"},
		err:  "",
	},
	{
		cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		blues: []string{"mem", "arch"},
		err:   "",
	},
	{
		cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		reds:  []string{"mem", "arch"},
		blues: []string{"instance-type"},
		err:   `ambiguous constraints: "arch" overlaps with "instance-type"`,
	},
	{
		cons:  "root-disk=8G mem=4G arch=amd64 cpu-cores=4 instance-type=foo",
		reds:  []string{"instance-type"},
		blues: []string{"mem", "arch"},
		err:   `ambiguous constraints: "arch" overlaps with "instance-type"`,
	},
	{
		cons:  "root-disk=8G mem=4G cpu-cores=4 instance-type=foo",
		reds:  []string{"mem", "arch"},
		blues: []string{"instance-type"},
		err:   `ambiguous constraints: "mem" overlaps with "instance-type"`,
	},
	{
		cons:  "arch=amd64 mem=4G cpu-cores=4",
		vocab: map[string][]interface{}{"arch": []interface{}{"amd64", "i386"}},
	},
	{
		cons:  "mem=4G cpu-cores=4",
		vocab: map[string][]interface{}{"cpu-cores": []interface{}{2, 4, 8}},
	},
	{
		cons:  "mem=4G instance-type=foo",
		vocab: map[string][]interface{}{"instance-type": []interface{}{"foo", "bar"}},
	},
	{
		cons:  "mem=4G tags=foo,bar",
		vocab: map[string][]interface{}{"tags": []interface{}{"foo", "bar", "another"}},
	},
	{
		cons:  "arch=i386 mem=4G cpu-cores=4",
		vocab: map[string][]interface{}{"arch": []interface{}{"amd64"}},
		err:   "invalid constraint value: arch=i386\nvalid values are:.*",
	},
	{
		cons:  "mem=4G cpu-cores=5",
		vocab: map[string][]interface{}{"cpu-cores": []interface{}{2, 4, 8}},
		err:   "invalid constraint value: cpu-cores=5\nvalid values are:.*",
	},
	{
		cons:  "mem=4G instance-type=foo",
		vocab: map[string][]interface{}{"instance-type": []interface{}{"bar"}},
		err:   "invalid constraint value: instance-type=foo\nvalid values are:.*",
	},
	{
		cons:  "mem=4G tags=foo,other",
		vocab: map[string][]interface{}{"tags": []interface{}{"foo", "bar", "another"}},
		err:   "invalid constraint value: tags=other\nvalid values are:.*",
	},
	{
		cons: "arch=i386 mem=4G instance-type=foo",
		vocab: map[string][]interface{}{
			"instance-type": []interface{}{"foo", "bar"},
			"arch":          []interface{}{"amd64", "i386"}},
	},
}

func (s *validationSuite) TestValidation(c *gc.C) {
	for i, t := range validationTests {
		c.Logf("test %d", i)
		validator := constraints.NewValidator()
		validator.RegisterUnsupported(t.unsupported)
		validator.RegisterConflicts(t.reds, t.blues)
		for a, v := range t.vocab {
			validator.RegisterVocabulary(a, v)
		}
		cons := constraints.MustParse(t.cons)
		unsupported, err := validator.Validate(cons)
		if t.err == "" {
			c.Assert(err, gc.IsNil)
			c.Assert(unsupported, jc.SameContents, t.unsupported)
		} else {
			c.Assert(err, gc.ErrorMatches, t.err)
		}
	}
}

var mergeTests = []struct {
	desc         string
	consFallback string
	cons         string
	unsupported  []string
	reds         []string
	blues        []string
	expected     string
}{
	{
		desc: "empty all round",
	}, {
		desc:     "container with empty fallback",
		cons:     "container=lxc",
		expected: "container=lxc",
	}, {
		desc:         "container from fallback",
		consFallback: "container=lxc",
		expected:     "container=lxc",
	}, {
		desc:     "arch with empty fallback",
		cons:     "arch=amd64",
		expected: "arch=amd64",
	}, {
		desc:         "arch with ignored fallback",
		cons:         "arch=amd64",
		consFallback: "arch=i386",
		expected:     "arch=amd64",
	}, {
		desc:         "arch from fallback",
		consFallback: "arch=i386",
		expected:     "arch=i386",
	}, {
		desc:     "instance type with empty fallback",
		cons:     "instance-type=foo",
		expected: "instance-type=foo",
	}, {
		desc:         "instance type with ignored fallback",
		cons:         "instance-type=foo",
		consFallback: "instance-type=bar",
		expected:     "instance-type=foo",
	}, {
		desc:         "instance type from fallback",
		consFallback: "instance-type=foo",
		expected:     "instance-type=foo",
	}, {
		desc:     "cpu-cores with empty fallback",
		cons:     "cpu-cores=2",
		expected: "cpu-cores=2",
	}, {
		desc:         "cpu-cores with ignored fallback",
		cons:         "cpu-cores=4",
		consFallback: "cpu-cores=8",
		expected:     "cpu-cores=4",
	}, {
		desc:         "cpu-cores from fallback",
		consFallback: "cpu-cores=8",
		expected:     "cpu-cores=8",
	}, {
		desc:     "cpu-power with empty fallback",
		cons:     "cpu-power=100",
		expected: "cpu-power=100",
	}, {
		desc:         "cpu-power with ignored fallback",
		cons:         "cpu-power=100",
		consFallback: "cpu-power=200",
		expected:     "cpu-power=100",
	}, {
		desc:         "cpu-power from fallback",
		consFallback: "cpu-power=200",
		expected:     "cpu-power=200",
	}, {
		desc:     "tags with empty fallback",
		cons:     "tags=foo,bar",
		expected: "tags=foo,bar",
	}, {
		desc:         "tags with ignored fallback",
		cons:         "tags=foo,bar",
		consFallback: "tags=baz",
		expected:     "tags=foo,bar",
	}, {
		desc:         "tags from fallback",
		consFallback: "tags=foo,bar",
		expected:     "tags=foo,bar",
	}, {
		desc:         "tags inital empty",
		cons:         "tags=",
		consFallback: "tags=foo,bar",
		expected:     "tags=",
	}, {
		desc:     "mem with empty fallback",
		cons:     "mem=4G",
		expected: "mem=4G",
	}, {
		desc:         "mem with ignored fallback",
		cons:         "mem=4G",
		consFallback: "mem=8G",
		expected:     "mem=4G",
	}, {
		desc:         "mem from fallback",
		consFallback: "mem=8G",
		expected:     "mem=8G",
	}, {
		desc:     "root-disk with empty fallback",
		cons:     "root-disk=4G",
		expected: "root-disk=4G",
	}, {
		desc:         "root-disk with ignored fallback",
		cons:         "root-disk=4G",
		consFallback: "root-disk=8G",
		expected:     "root-disk=4G",
	}, {
		desc:         "root-disk from fallback",
		consFallback: "root-disk=8G",
		expected:     "root-disk=8G",
	}, {
		desc:         "non-overlapping mix",
		cons:         "root-disk=8G mem=4G arch=amd64",
		consFallback: "cpu-power=1000 cpu-cores=4",
		expected:     "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
	}, {
		desc:         "overlapping mix",
		cons:         "root-disk=8G mem=4G arch=amd64",
		consFallback: "cpu-power=1000 cpu-cores=4 mem=8G",
		expected:     "root-disk=8G mem=4G arch=amd64 cpu-power=1000 cpu-cores=4",
	}, {
		desc:         "fallback only, no conflicts",
		consFallback: "root-disk=8G cpu-cores=4 instance-type=foo",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=foo",
	}, {
		desc:     "no fallback, no conflicts",
		cons:     "root-disk=8G cpu-cores=4 instance-type=foo",
		reds:     []string{"mem", "arch"},
		blues:    []string{"instance-type"},
		expected: "root-disk=8G cpu-cores=4 instance-type=foo",
	}, {
		desc:         "conflict value from override",
		consFallback: "root-disk=8G instance-type=foo",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "unsupported attributes ignored",
		consFallback: "root-disk=8G instance-type=foo",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		unsupported:  []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "red conflict masked from fallback",
		consFallback: "root-disk=8G mem=4G",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "second red conflict masked from fallback",
		consFallback: "root-disk=8G arch=amd64",
		cons:         "root-disk=8G cpu-cores=4 instance-type=bar",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 instance-type=bar",
	}, {
		desc:         "blue conflict masked from fallback",
		consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
		cons:         "root-disk=8G mem=4G",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 mem=4G",
	}, {
		desc:         "both red conflicts used, blue mased from fallback",
		consFallback: "root-disk=8G cpu-cores=4 instance-type=bar",
		cons:         "root-disk=8G arch=amd64 mem=4G",
		reds:         []string{"mem", "arch"},
		blues:        []string{"instance-type"},
		expected:     "root-disk=8G cpu-cores=4 arch=amd64 mem=4G",
	},
}

func (s *validationSuite) TestMerge(c *gc.C) {
	for i, t := range mergeTests {
		c.Logf("test %d: %s", i, t.desc)
		validator := constraints.NewValidator()
		validator.RegisterConflicts(t.reds, t.blues)
		consFallback := constraints.MustParse(t.consFallback)
		cons := constraints.MustParse(t.cons)
		merged, err := validator.Merge(consFallback, cons)
		c.Assert(err, gc.IsNil)
		expected := constraints.MustParse(t.expected)
		c.Check(merged, gc.DeepEquals, expected)
	}
}

func (s *validationSuite) TestMergeError(c *gc.C) {
	validator := constraints.NewValidator()
	validator.RegisterConflicts([]string{"instance-type"}, []string{"mem"})
	consFallback := constraints.MustParse("instance-type=foo mem=4G")
	cons := constraints.MustParse("cpu-cores=2")
	_, err := validator.Merge(consFallback, cons)
	c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "mem" overlaps with "instance-type"`)
	_, err = validator.Merge(cons, consFallback)
	c.Assert(err, gc.ErrorMatches, `ambiguous constraints: "mem" overlaps with "instance-type"`)
}
<|endoftext|>