<commit_before>package libcmdline

import (
	"fmt"
	"regexp"
	"strings"
	"text/tabwriter"
	"text/template"

	"github.com/codegangsta/cli"
	"github.com/keybase/client/go/libkb"
)

type Command interface {
	libkb.Command
	ParseArgv(*cli.Context) error // A command-specific parse-args
	Run() error                   // Actually run the command (finally!)
	RunClient() error             // Run in client mode
}

type CommandLine struct {
	app        *cli.App
	ctx        *cli.Context
	cmd        Command
	name       string // the name of the chosen command
	defaultCmd string
}

func (p CommandLine) GetHome() string {
	return p.GetGString("home")
}
func (p CommandLine) GetServerUri() string {
	return p.GetGString("server")
}
func (p CommandLine) GetConfigFilename() string {
	return p.GetGString("config")
}
func (p CommandLine) GetSessionFilename() string {
	return p.GetGString("session")
}
func (p CommandLine) GetDbFilename() string {
	return p.GetGString("db")
}
func (p CommandLine) GetDebug() (bool, bool) {
	return p.GetBool("debug", true)
}
func (p CommandLine) GetPgpFingerprint() *libkb.PgpFingerprint {
	return libkb.PgpFingerprintFromHexNoError(p.GetGString("fingerprint"))
}
func (p CommandLine) GetEmail() string {
	return p.GetGString("email")
}
func (p CommandLine) GetProxy() string {
	return p.GetGString("proxy")
}
func (p CommandLine) GetUsername() string {
	return p.GetGString("username")
}
func (p CommandLine) GetPlainLogging() (bool, bool) {
	return p.GetBool("plain-logging", true)
}
func (p CommandLine) GetGpgHome() string {
	return p.GetGString("gpg-home")
}
func (p CommandLine) GetApiDump() (bool, bool) {
	return p.GetBool("api-dump", true)
}
func (p CommandLine) GetPinentry() string {
	return p.GetGString("pinentry")
}
func (p CommandLine) GetGString(s string) string {
	return p.ctx.GlobalString(s)
}
func (p CommandLine) GetGInt(s string) int {
	return p.ctx.GlobalInt(s)
}
func (p CommandLine) GetGpg() string {
	return p.GetGString("gpg")
}
func (p CommandLine) GetSecretKeyringTemplate() string {
	return p.GetGString("secret-keyring")
}
func (p CommandLine) GetSocketFile() string {
	return p.GetGString("socket-file")
}
func (p CommandLine) GetPidFile() string {
	return p.GetGString("pid-file")
}
func (p CommandLine) GetGpgOptions() []string {
	var ret []string
	s := p.GetGString("gpg-options")
	if len(s) > 0 {
		ret = regexp.MustCompile(`\s+`).Split(s, -1)
	}
	return ret
}
func (p CommandLine) GetGpgDisabled() (bool, bool) {
	return p.GetBool("gpg-disabled", true)
}
func (p CommandLine) GetMerkleKeyFingerprints() []string {
	s := p.GetGString("merkle-key-fingerprints")
	if len(s) != 0 {
		return strings.Split(s, ":")
	} else {
		return nil
	}
}
func (p CommandLine) GetUserCacheSize() (int, bool) {
	ret := p.GetGInt("user-cache-size")
	if ret != 0 {
		return ret, true
	} else {
		return 0, false
	}
}
func (p CommandLine) GetProofCacheSize() (int, bool) {
	ret := p.GetGInt("proof-cache-size")
	if ret != 0 {
		return ret, true
	} else {
		return 0, false
	}
}
func (p CommandLine) GetDaemonPort() (ret int, set bool) {
	if ret = p.GetGInt("daemon-port"); ret != 0 {
		set = true
	}
	return
}

func (p CommandLine) GetStandalone() (bool, bool) {
	return p.GetBool("standalone", true)
}

func (p CommandLine) GetLocalRpcDebug() string {
	return p.GetGString("local-rpc-debug")
}

func (p CommandLine) GetBool(s string, glbl bool) (bool, bool) {
	var v bool
	if glbl {
		v = p.ctx.GlobalBool(s)
	} else {
		v = p.ctx.Bool(s)
	}
	return v, v
}

type CmdBaseHelp struct {
	ctx *cli.Context
}

func (c *CmdBaseHelp) GetUsage() libkb.Usage {
	return libkb.Usage{}
}
func (c *CmdBaseHelp) ParseArgv(*cli.Context) error { return nil }

type CmdGeneralHelp struct {
	CmdBaseHelp
}

func (c *CmdBaseHelp) RunClient() error { return c.Run() }

// This is a hack to work around codegangsta/cli destroying the HelpPrinter
// object.
func (c *CmdBaseHelp) MakeHelpPrinter() {
	if cli.HelpPrinter != nil {
		return
	}
	funcMap := template.FuncMap{
		"join": strings.Join,
	}

	cli.HelpPrinter = func(templ string, data interface{}) {
		w := tabwriter.NewWriter(c.ctx.App.Writer, 0, 8, 1, '\t', 0)
		t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
		err := t.Execute(w, data)
		if err != nil {
			panic(err)
		}
		w.Flush()
	}
}

func (c *CmdBaseHelp) Run() error {
	c.MakeHelpPrinter()
	cli.ShowAppHelp(c.ctx)
	return nil
}

type CmdSpecificHelp struct {
	CmdBaseHelp
	name string
}

func (c CmdSpecificHelp) Run() error {
	cli.ShowCommandHelp(c.ctx, c.name)
	return nil
}

func NewCommandLine(addHelp bool) *CommandLine {
	app := cli.NewApp()
	ret := &CommandLine{app: app}
	ret.PopulateApp(addHelp)
	return ret
}

func (cl *CommandLine) PopulateApp(addHelp bool) {
	app := cl.app
	app.Name = "keybase"
	app.Version = libkb.CLIENT_VERSION
	app.Usage = "control keybase either with 1-off commands, " +
		"or start a daemon"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "home, H",
			Usage: "specify an (alternate) home directory",
		},
		cli.StringFlag{
			Name:  "server, s",
			Usage: fmt.Sprintf("specify server API (default: %s)", libkb.SERVER_URL),
		},
		cli.StringFlag{
			Name:  "config, c",
			Usage: "specify an (alternate) master config file",
		},
		cli.StringFlag{
			Name:  "session",
			Usage: "specify an alternate session data file",
		},
		cli.StringFlag{
			Name:  "db",
			Usage: "specify an alternate local DB location",
		},
		cli.StringFlag{
			Name:  "api-uri-path-prefix",
			Usage: "specify an alternate API URI path prefix",
		},
		cli.StringFlag{
			Name:  "username, u",
			Usage: "specify Keybase username of the current user",
		},
		cli.StringFlag{
			Name:  "uid, i",
			Usage: "specify Keybase UID for current user",
		},
		cli.StringFlag{
			Name:  "pinentry",
			Usage: "specify a path to find a pinentry program",
		},
		cli.StringFlag{
			Name:  "secret-keyring",
			Usage: "location of the Keybase secret-keyring (P3SKB-encoded)",
		},
		cli.StringFlag{
			Name:  "socket-file",
			Usage: "location of the keybased socket-file",
		},
		cli.StringFlag{
			Name:  "pid-file",
			Usage: "location of the keybased pid-file (to ensure only one running daemon)",
		},
		cli.StringFlag{
			Name: "proxy",
			Usage: "specify an HTTP(s) proxy to ship all Web " +
				"requests over",
		},
		cli.BoolFlag{
			Name:  "debug, d",
			Usage: "enable debugging mode",
		},
		cli.StringFlag{
			Name:  "email",
			Usage: "specify your email address for login/signup purposes",
		},
		cli.BoolFlag{
			Name:  "plain-logging, L",
			Usage: "plain logging mode (no colors)",
		},
		cli.StringFlag{
			Name:  "pgpdir, gpgdir",
			Usage: "specify a PGP directory (default is ~/.gnupg)",
		},
		cli.BoolFlag{
			Name:  "api-dump",
			Usage: "dump API call internals",
		},
		cli.StringFlag{
			Name:  "merkle-key-fingerprints",
			Usage: "Set of admissable Merkle Tree fingerprints (colon-separated)",
		},
		cli.IntFlag{
			Name:  "user-cache-size",
			Usage: "number of User entries to cache",
		},
		cli.IntFlag{
			Name:  "proof-cache-size",
			Usage: "number of proof entries to cache",
		},
		cli.StringFlag{
			Name:  "gpg",
			Usage: "Path to GPG client (optional for exporting keys)",
		},
		cli.StringFlag{
			Name:  "gpg-options",
			Usage: "Options to use when calling GPG",
		},
		cli.BoolFlag{
			Name:  "gpg-disabled",
			Usage: "Disable GPG support",
		},
		cli.IntFlag{
			Name:  "daemon-port",
			Usage: "specify a daemon port on 127.0.0.1",
		},
		cli.BoolFlag{
			Name:  "standalone",
			Usage: "use the client without any daemon support",
		},
		cli.StringFlag{
			Name:  "local-rpc-debug",
			Usage: "use to debug local RPC",
		},
		cli.StringFlag{
			Name:  "device-kid",
			Usage: "specify per-device KID",
		},
		cli.StringFlag{
			Name:  "device-id",
			Usage: "specify the device ID",
		},
	}

	// Finally, add help if we asked for it
	if addHelp {
		app.Action = func(c *cli.Context) {
			cl.cmd = &CmdGeneralHelp{CmdBaseHelp{c}}
			cl.ctx = c
			cl.name = "help"
		}
	}
}

func (cl *CommandLine) AddCommands(cmds []cli.Command) {
	cl.app.Commands = cmds
}

func (cl *CommandLine) SetDefaultCommand(name string, cmd Command) {
	cl.defaultCmd = name
	cl.app.Action = func(c *cli.Context) {
		cl.cmd = cmd
		cl.ctx = c
		cl.name = name
	}
}

// Called back from inside our subcommands, when they're picked...
func (p *CommandLine) ChooseCommand(cmd Command, name string, ctx *cli.Context) {
	p.cmd = cmd
	p.name = name
	p.ctx = ctx
}

func (p *CommandLine) Parse(args []string) (cmd Command, err error) {
	// This is suboptimal, but the default help action when there are
	// no args crashes.
	// (cli sets HelpPrinter to nil when p.app.Run(...) returns.)
	if len(args) == 1 && p.defaultCmd == "help" {
		args = append(args, p.defaultCmd)
	}

	// Actually pick a command
	err = p.app.Run(args)

	// Should not be populated
	cmd = p.cmd

	if err != nil || cmd == nil {
		return
	}

	// cli.HelpPrinter is nil here...anything that needs it will panic.

	// If we failed to parse arguments properly, switch to the help command
	if err = p.cmd.ParseArgv(p.ctx); err != nil {
		libkb.G.Log.Errorf("In '%s': %s", p.name, err.Error())
		cmd = &CmdSpecificHelp{CmdBaseHelp{p.ctx}, p.name}
	}

	return
}
<commit_msg>delete a couple unused global flags<commit_after>package libcmdline

import (
	"fmt"
	"regexp"
	"strings"
	"text/tabwriter"
	"text/template"

	"github.com/codegangsta/cli"
	"github.com/keybase/client/go/libkb"
)

type Command interface {
	libkb.Command
	ParseArgv(*cli.Context) error // A command-specific parse-args
	Run() error                   // Actually run the command (finally!)
	RunClient() error             // Run in client mode
}

type CommandLine struct {
	app        *cli.App
	ctx        *cli.Context
	cmd        Command
	name       string // the name of the chosen command
	defaultCmd string
}

func (p CommandLine) GetHome() string {
	return p.GetGString("home")
}
func (p CommandLine) GetServerUri() string {
	return p.GetGString("server")
}
func (p CommandLine) GetConfigFilename() string {
	return p.GetGString("config")
}
func (p CommandLine) GetSessionFilename() string {
	return p.GetGString("session")
}
func (p CommandLine) GetDbFilename() string {
	return p.GetGString("db")
}
func (p CommandLine) GetDebug() (bool, bool) {
	return p.GetBool("debug", true)
}
func (p CommandLine) GetPgpFingerprint() *libkb.PgpFingerprint {
	return libkb.PgpFingerprintFromHexNoError(p.GetGString("fingerprint"))
}
func (p CommandLine) GetEmail() string {
	return p.GetGString("email")
}
func (p CommandLine) GetProxy() string {
	return p.GetGString("proxy")
}
func (p CommandLine) GetUsername() string {
	return p.GetGString("username")
}
func (p CommandLine) GetPlainLogging() (bool, bool) {
	return p.GetBool("plain-logging", true)
}
func (p CommandLine) GetGpgHome() string {
	return p.GetGString("gpg-home")
}
func (p CommandLine) GetApiDump() (bool, bool) {
	return p.GetBool("api-dump", true)
}
func (p CommandLine) GetPinentry() string {
	return p.GetGString("pinentry")
}
func (p CommandLine) GetGString(s string) string {
	return p.ctx.GlobalString(s)
}
func (p CommandLine) GetGInt(s string) int {
	return p.ctx.GlobalInt(s)
}
func (p CommandLine) GetGpg() string {
	return p.GetGString("gpg")
}
func (p CommandLine) GetSecretKeyringTemplate() string {
	return p.GetGString("secret-keyring")
}
func (p CommandLine) GetSocketFile() string {
	return p.GetGString("socket-file")
}
func (p CommandLine) GetPidFile() string {
	return p.GetGString("pid-file")
}
func (p CommandLine) GetGpgOptions() []string {
	var ret []string
	s := p.GetGString("gpg-options")
	if len(s) > 0 {
		ret = regexp.MustCompile(`\s+`).Split(s, -1)
	}
	return ret
}
func (p CommandLine) GetGpgDisabled() (bool, bool) {
	return p.GetBool("gpg-disabled", true)
}
func (p CommandLine) GetMerkleKeyFingerprints() []string {
	s := p.GetGString("merkle-key-fingerprints")
	if len(s) != 0 {
		return strings.Split(s, ":")
	} else {
		return nil
	}
}
func (p CommandLine) GetUserCacheSize() (int, bool) {
	ret := p.GetGInt("user-cache-size")
	if ret != 0 {
		return ret, true
	} else {
		return 0, false
	}
}
func (p CommandLine) GetProofCacheSize() (int, bool) {
	ret := p.GetGInt("proof-cache-size")
	if ret != 0 {
		return ret, true
	} else {
		return 0, false
	}
}
func (p CommandLine) GetDaemonPort() (ret int, set bool) {
	if ret = p.GetGInt("daemon-port"); ret != 0 {
		set = true
	}
	return
}

func (p CommandLine) GetStandalone() (bool, bool) {
	return p.GetBool("standalone", true)
}

func (p CommandLine) GetLocalRpcDebug() string {
	return p.GetGString("local-rpc-debug")
}

func (p CommandLine) GetBool(s string, glbl bool) (bool, bool) {
	var v bool
	if glbl {
		v = p.ctx.GlobalBool(s)
	} else {
		v = p.ctx.Bool(s)
	}
	return v, v
}

type CmdBaseHelp struct {
	ctx *cli.Context
}

func (c *CmdBaseHelp) GetUsage() libkb.Usage {
	return libkb.Usage{}
}
func (c *CmdBaseHelp) ParseArgv(*cli.Context) error { return nil }

type CmdGeneralHelp struct {
	CmdBaseHelp
}

func (c *CmdBaseHelp) RunClient() error { return c.Run() }

// This is a hack to work around codegangsta/cli destroying the HelpPrinter
// object.
func (c *CmdBaseHelp) MakeHelpPrinter() {
	if cli.HelpPrinter != nil {
		return
	}
	funcMap := template.FuncMap{
		"join": strings.Join,
	}

	cli.HelpPrinter = func(templ string, data interface{}) {
		w := tabwriter.NewWriter(c.ctx.App.Writer, 0, 8, 1, '\t', 0)
		t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
		err := t.Execute(w, data)
		if err != nil {
			panic(err)
		}
		w.Flush()
	}
}

func (c *CmdBaseHelp) Run() error {
	c.MakeHelpPrinter()
	cli.ShowAppHelp(c.ctx)
	return nil
}

type CmdSpecificHelp struct {
	CmdBaseHelp
	name string
}

func (c CmdSpecificHelp) Run() error {
	cli.ShowCommandHelp(c.ctx, c.name)
	return nil
}

func NewCommandLine(addHelp bool) *CommandLine {
	app := cli.NewApp()
	ret := &CommandLine{app: app}
	ret.PopulateApp(addHelp)
	return ret
}

func (cl *CommandLine) PopulateApp(addHelp bool) {
	app := cl.app
	app.Name = "keybase"
	app.Version = libkb.CLIENT_VERSION
	app.Usage = "control keybase either with 1-off commands, " +
		"or start a daemon"

	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "home, H",
			Usage: "specify an (alternate) home directory",
		},
		cli.StringFlag{
			Name:  "server, s",
			Usage: fmt.Sprintf("specify server API (default: %s)", libkb.SERVER_URL),
		},
		cli.StringFlag{
			Name:  "config, c",
			Usage: "specify an (alternate) master config file",
		},
		cli.StringFlag{
			Name:  "session",
			Usage: "specify an alternate session data file",
		},
		cli.StringFlag{
			Name:  "db",
			Usage: "specify an alternate local DB location",
		},
		cli.StringFlag{
			Name:  "api-uri-path-prefix",
			Usage: "specify an alternate API URI path prefix",
		},
		cli.StringFlag{
			Name:  "username, u",
			Usage: "specify Keybase username of the current user",
		},
		cli.StringFlag{
			Name:  "uid, i",
			Usage: "specify Keybase UID for current user",
		},
		cli.StringFlag{
			Name:  "pinentry",
			Usage: "specify a path to find a pinentry program",
		},
		cli.StringFlag{
			Name:  "secret-keyring",
			Usage: "location of the Keybase secret-keyring (P3SKB-encoded)",
		},
		cli.StringFlag{
			Name:  "socket-file",
			Usage: "location of the keybased socket-file",
		},
		cli.StringFlag{
			Name:  "pid-file",
			Usage: "location of the keybased pid-file (to ensure only one running daemon)",
		},
		cli.StringFlag{
			Name: "proxy",
			Usage: "specify an HTTP(s) proxy to ship all Web " +
				"requests over",
		},
		cli.BoolFlag{
			Name:  "debug, d",
			Usage: "enable debugging mode",
		},
		cli.StringFlag{
			Name:  "email",
			Usage: "specify your email address for login/signup purposes",
		},
		cli.BoolFlag{
			Name:  "plain-logging, L",
			Usage: "plain logging mode (no colors)",
		},
		cli.StringFlag{
			Name:  "pgpdir, gpgdir",
			Usage: "specify a PGP directory (default is ~/.gnupg)",
		},
		cli.BoolFlag{
			Name:  "api-dump",
			Usage: "dump API call internals",
		},
		cli.StringFlag{
			Name:  "merkle-key-fingerprints",
			Usage: "Set of admissable Merkle Tree fingerprints (colon-separated)",
		},
		cli.IntFlag{
			Name:  "user-cache-size",
			Usage: "number of User entries to cache",
		},
		cli.IntFlag{
			Name:  "proof-cache-size",
			Usage: "number of proof entries to cache",
		},
		cli.StringFlag{
			Name:  "gpg",
			Usage: "Path to GPG client (optional for exporting keys)",
		},
		cli.StringFlag{
			Name:  "gpg-options",
			Usage: "Options to use when calling GPG",
		},
		cli.BoolFlag{
			Name:  "gpg-disabled",
			Usage: "Disable GPG support",
		},
		cli.IntFlag{
			Name:  "daemon-port",
			Usage: "specify a daemon port on 127.0.0.1",
		},
		cli.BoolFlag{
			Name:  "standalone",
			Usage: "use the client without any daemon support",
		},
		cli.StringFlag{
			Name:  "local-rpc-debug",
			Usage: "use to debug local RPC",
		},
	}

	// Finally, add help if we asked for it
	if addHelp {
		app.Action = func(c *cli.Context) {
			cl.cmd = &CmdGeneralHelp{CmdBaseHelp{c}}
			cl.ctx = c
			cl.name = "help"
		}
	}
}

func (cl *CommandLine) AddCommands(cmds []cli.Command) {
	cl.app.Commands = cmds
}

func (cl *CommandLine) SetDefaultCommand(name string, cmd Command) {
	cl.defaultCmd = name
	cl.app.Action = func(c *cli.Context) {
		cl.cmd = cmd
		cl.ctx = c
		cl.name = name
	}
}

// Called back from inside our subcommands, when they're picked...
func (p *CommandLine) ChooseCommand(cmd Command, name string, ctx *cli.Context) {
	p.cmd = cmd
	p.name = name
	p.ctx = ctx
}

func (p *CommandLine) Parse(args []string) (cmd Command, err error) {
	// This is suboptimal, but the default help action when there are
	// no args crashes.
	// (cli sets HelpPrinter to nil when p.app.Run(...) returns.)
	if len(args) == 1 && p.defaultCmd == "help" {
		args = append(args, p.defaultCmd)
	}

	// Actually pick a command
	err = p.app.Run(args)

	// Should not be populated
	cmd = p.cmd

	if err != nil || cmd == nil {
		return
	}

	// cli.HelpPrinter is nil here...anything that needs it will panic.

	// If we failed to parse arguments properly, switch to the help command
	if err = p.cmd.ParseArgv(p.ctx); err != nil {
		libkb.G.Log.Errorf("In '%s': %s", p.name, err.Error())
		cmd = &CmdSpecificHelp{CmdBaseHelp{p.ctx}, p.name}
	}

	return
}
<|endoftext|>


<commit_before>package tuplespace

import (
	"reflect"
	"testing"
)

var testTemplateOneField Template
var testTemplateFourFields Template

func init() {
	// Create simple template. Default value of matchNumberOfFields is true
	testTemplateOneField = CreateTemplate("Test Field")
	// Create simple template with four fields.
	testTemplateFourFields = CreateTemplate("Field 1", 2, 3.14, false)
}

// Test to see if template is creating correct.
func TestCreateTemplate(t *testing.T) {
	fields := make([]interface{}, 4)
	fields[0] = "Field 1"
	fields[1] = 2
	fields[2] = 3.14
	fields[3] = false

	// First argument is true, as it is the default value when creating a
	// template.
	var testTemplateManual = Template{true, fields}

	// Test that the two templates are equal.
	templatesEqual := reflect.DeepEqual(testTemplateManual, testTemplateFourFields)

	if !templatesEqual {
		t.Errorf("CreateTemplate() gave %+v, should be %+v", testTemplateFourFields, testTemplateManual)
	}
}

// Test that will check if the correct boolean value of matchNumberOfFields is
// returned.
func TestTemplateGetMatchNumberOfFields(t *testing.T) {
	defaultvalueOfMatchNumberOfField := true
	valueOfMatchNumberOfFields := testTemplateOneField.getMatchNumberOfFields()

	if valueOfMatchNumberOfFields != defaultvalueOfMatchNumberOfField {
		t.Errorf("testTemplate's matchNumberOfFields was not returned correctly, should have been true.")
	}
}

// Test that will change the boolean value of matchNumberOfFields back and forth.
func TestChangeMatchNumberOfFields(t *testing.T) {
	// Make a copy of the constant defined template with default value of
	// matchNumberOfFields to true.
	testTemplate := testTemplateOneField

	if testTemplate.getMatchNumberOfFields() != true {
		t.Errorf("testTemplate's matchNumberOfFields was not set to the correct default value being true.")
	}

	// Set boolean value to false.
	testTemplate.DontMatchNumberOfFields()

	if testTemplate.getMatchNumberOfFields() != false {
		t.Errorf("testTemplate's matchNumberOfFields was not set to the correct value being false by method DontMatchNumberOfFields().")
	}

	// Set boolean value back to true.
	testTemplate.DoMatchNumberOfFields()

	if testTemplate.getMatchNumberOfFields() != true {
		t.Errorf("testTemplate's matchNumberOfFields was not set to the correct value being true by method MatchNumberOfFields().")
	}
}

// Test to see if template has correct length.
func TestTemplateLength(t *testing.T) {
	// Create template with same length as defined by variable.
	var templateLength = 4
	testTemplateLength := testTemplateFourFields.length()
	if templateLength != testTemplateLength {
		t.Errorf("Length(%+v) == %d, should be %d", testTemplateFourFields, testTemplateLength, templateLength)
	}
}

// Test to see if the correct template field is returned.
func TestTemplateGetFieldAt(t *testing.T) {
	var wantedField = false
	templateField := testTemplateFourFields.getFieldAt(3)
	if wantedField != templateField {
		t.Errorf("GetFieldAt(%d) on template: %+v == %v, should be %v", 4, testTemplateFourFields, templateField, wantedField)
	}
}
<commit_msg>Delete template_test.go<commit_after><|endoftext|>


<commit_before>package main

/*
MODULE Solve;

  REVISION
  --------
   2 Mar 05 -- Added prompts to remind me of the file format.
   3 Mar 05 -- Made version 2 write lines like eqn w/o =.
   4 Mar 05 -- Don't need N as 1st line now.
  26 Feb 06 -- Will reject non-numeric entries and allows <tab> as delim.
  24 Dec 16 -- Converted to Go.
  31 Jul 20 -- Starting conversion to use gonum.org/mat code
*/

import (
	"bufio"
	"fmt"
	"getcommandline"
	"gonum.org/v1/gonum/mat" // from memory.  This needs to be confirmed.
	"io"
	mymat "mat"
	"math"
	"os"
	"path/filepath"
  "strconv"
  "strings"
	"tokenize"
)

const LastCompiled = "July 31, 2020"
const MaxN = 30
const toleranceFactor = 1e-6

// IM (inputMatrix) is not square because the B column vector is in last column of IM.
//                                        was TYPE MaxRealArray = ARRAY [1..MaxN],[1..MaxN+1] OF LONGREAL; in M-2 code.

// type Matrix2D [][]float64;  not needed here.  It's internal to mymat package.

func main() {
	/* ------------------------------------------------------------------------
	                           MAIN PROGRAM
	------------------------------------------------------------------------*/

	fmt.Println(" Equation solver written in Go.  Last compiled ", LastCompiled)
	fmt.Println()

	if len(os.Args) <= 1 {
		fmt.Println(" Usage: solve <filename>")
		fmt.Println(" Solves vector equation A * X = B; and A is a square coef matrix.")
		fmt.Println(" N is determined by number of rows and B value is last on each line.")
		os.Exit(0)
	}

	commandline := getcommandline.GetCommandLineString()
	cleancommandline := filepath.Clean(commandline)
	fmt.Println(" filename on command line is ", cleancommandline)

	//  cleancommandline = "xy1.txt";

	infile, err := os.Open(cleancommandline)
	if err != nil {
		fmt.Println(" Cannot open input file.  Does it exist?  Error is ", err)
		os.Exit(1)
	}

	defer infile.Close()
	scanner := bufio.NewScanner(infile)

	IM := mymat.NewMatrix(MaxN, MaxN+1) // IM is input matrix
	IM = mymat.Zero(IM)

	lines := 0
CountLinesLoop:
	for { // read, count and process lines
		for n := 0; n < MaxN; n++ {
			readSuccess := scanner.Scan()
			if readSuccess {
				// Non-numeric tokens are comments, and lines not beginning with a number are comment lines.
			} else {
				break CountLinesLoop // break outer loop
			} // if readSuccess

			inputline := scanner.Text()
			if readErr := scanner.Err(); readErr != nil {
				if readErr == io.EOF {
					break CountLinesLoop // break outer loop
				} else { // this may be redundant because of the readSuccess test
					break CountLinesLoop // break outer loop
				}
			}

			tokenize.INITKN(inputline)
			col := 0
			EOL := false
			for (EOL == false) && (n <= MaxN) { // read numbers into IM input matrix.
				token, EOL := tokenize.GETTKNREAL()
				if EOL {
					break
				}
				if token.State != tokenize.DGT {
					break
				} // treat remainder of line as a comment
				IM[lines][col] = token.Rsum // remember that IM is Input Matrix
				col++
			}
			if col > 0 { // allow comments by incrementing the pointer only when have numbers on the line.
				lines++
			}
		} // END for n
	} // END outer reading loop

	N := lines // Note: lines is 0 origin, and therefore so is N

	// Now need to create A and B matrices

	A := mymat.NewMatrix(N, N)
	B := mymat.NewMatrix(N, 1)
	for row := range A {
		for col := range A[0] {
			A[row][col] = IM[row][col]
		} // END FOR col
		B[row][0] = IM[row][N] // I have to keep remembering that [0,0] is the first row and col.
	} // END FOR row

	fmt.Println(" coef matrix A is:")
	ss := mymat.Write(A, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	fmt.Println(" Right hand side vector matrix B is:")
	ss = mymat.Write(B, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	ans := mymat.NewMatrix(N, N)
	ans = mymat.Solve(A, B) // Solve (ra1, ra2, ans, N, 1);
	fmt.Println("The solution X to AX = B using Solve is")
	ss = mymat.Write(ans, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	ans2 := mymat.NewMatrix(N, N)
	ans2 = mymat.GaussJ(A, B) // Solve (ra1, ra2, ans, N, 1);
	fmt.Println("The solution X to AX = B using GaussJ is")
	ss = mymat.Write(ans2, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	pause()

	// Check that the solution looks right.

	C := mymat.NewMatrix(N, 1)
	D := mymat.NewMatrix(N, 1)
	C = mymat.Mul(A, ans)
	D = mymat.Sub(B, C)

	fmt.Println("As a check, AX-B should be 0, and evaluates to")
	ss = mymat.Write(D, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	D = mymat.BelowSmallMakeZero(D)

	fmt.Println("As a check, AX-B should be all zeros after calling BelowSmall.  It evaluates to")
	ss = mymat.Write(D, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()
	fmt.Println()

	pause()

	// Now for gonum.org mat code

	fmt.Println(" Using gonum.org Solve")
	newA := mat.NewDense(N, N, nil)
	newB := mat.NewDense(N, 1, nil)
	for row := 0; row < N; row++ {
		for col := 0; col < N; col++ {
			newA.Set(row, col, A[row][col])
		} // END FOR col
		newB.Set(row, 0, B[row][0])
	} // END FOR row

	var newX mat.Dense // construct empty matrix
	mat.NewDense(N, N, nil)
	er := newX.Solve(newA, newB)
	if er != nil {
		fmt.Println(" Error from newX.Solve is", er)
	}
	ss = write(newX, 5)
	fmt.Println(" newX:")
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	ss = writeZero(newX, 5)
    fmt.Println(" newX:")
    for _, s := range ss {
      fmt.Print(s)
    }
    fmt.Println()

	fmt.Println()

	pause()

  // Now to make B a vector (well, a column vector, but still a vector) and then use vector routines.  Just to see if I can get it to work, too.

    vecB := mat.NewVecDense(N, nil)
    for i := 0; i < N; i++ {
      vecB.SetVec(i, newB.At(i, 0))
    }

    var vecX mat.VecDense
    er = vecX.SolveVec(newA, vecB)
    if er != nil {
      fmt.Println(" Error from vecX.Solve is", er)
    }

    fmt.Println(" B and X are now vectors instead of matricies")
    vectorB := *vecB
    ss = vectorWrite(vectorB, 5)
    fmt.Println(" B: ")
    for _, s := range ss {
      fmt.Print(s)
    }
    fmt.Println()
    fmt.Println()

    ss = vectorWrite(vecX, 5)
      fmt.Println(" X: ")
      for _, s := range ss {
        fmt.Print(s)
      }
      fmt.Println()
      fmt.Println()

/*
		  var inverseA mat.Dense
		  err = inverseA.Inverse(newA)
		  if err != nil {
			  fmt.Println(" Error from inverseA.Inverse is", err)
		  }
		  var newX2 mat.Dense // empty matrix is constructed
		  newX2.Mul(inverseA, newB)
		  ss = write(newX2, 5)
		  for _, s := range ss {
			  fmt.Print(s)
		  }

		  fmt.Println()
		  fmt.Println()

		  pause()

	  fmt.Println(" Do the gonum.org computations look right?  First newA * newX - newB by solve, then by inverse")

	  var newE, newF mat.Dense
	  newE.Mul(newA, newX) // compute newE = newA * newX
	  newE.Sub(newB)       // compute newA * newX - newB which should be all zeros
	  ss = write(newE)
	  for _, s := range ss {
		  fmt.Print(s)
	  }

	  ss = writeZero(newE)
	  for _, s := range ss {
		  fmt.Print(s)
	  }

	  fmt.Println()
	  fmt.Println()

	  pause()

	  newF.Mul(newA, newX2) // compute newF = NewA * newX2
	  newF.Sub(newB)        // compute newA * newX2 - newB which should also be all zeros
	  ss = write(newF)

	  for _, s := range ss {
		  fmt.Print(s)
	  }

	  ss = writeZero(newF)
	  for _, s := range ss {
		  fmt.Print(s)
	  }

	  fmt.Println()
	  fmt.Println()

 */
} // END Solve.

func pause() {
	scnr := bufio.NewScanner(os.Stdin)
	fmt.Print(" pausing ... hit <enter>")
	scnr.Scan()
	answer := scnr.Text()
	if err := scnr.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
		os.Exit(1)
	}
	ans := strings.TrimSpace(answer)
	ans = strings.ToUpper(ans)
	fmt.Println(ans)
}

func write(M mat.Dense, places int) []string {

	// Writes the r x c matrix M to a string slice which is returned.  Each string represents a field "places" characters wide.

    rows, cols := M.Dims()
    OutputStringSlice := make([]string, 0, rows * cols)

    for i := 0; i < rows; i++ {
          for j := 0; j < cols; j++ {
              ss := strconv.FormatFloat(M.At(i, j), 'G', places, 64)
              OutputStringSlice = append(OutputStringSlice, fmt.Sprintf("%10s", ss))
          } // END FOR j
          OutputStringSlice = append(OutputStringSlice, "\n")
    } // END FOR i
    OutputStringSlice = append(OutputStringSlice, "\n")
    return OutputStringSlice
} // END write

func writeZero(M mat.Dense, places int) []string {

	// Writes the r x c matrix M to a string slice which is returned.  Each string represents a field "places" characters wide.

    rows, cols := M.Dims()
	OutputStringSlice := make([]string, 0, rows * cols)

	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			v := M.At(i, j)
			ss := ""
			if math.Abs(v) < toleranceFactor {
				ss = "0.0"
			} else {
				ss = strconv.FormatFloat(M.At(i, j), 'G', places, 64)
			}
			OutputStringSlice = append(OutputStringSlice, fmt.Sprintf("%10s", ss))
		} // END FOR j
		OutputStringSlice = append(OutputStringSlice, "\n")
	} // END FOR i
	OutputStringSlice = append(OutputStringSlice, "\n")
	return OutputStringSlice
} // END write

func vectorWrite(M mat.VecDense, places int) []string {
  rows, cols := M.Dims()
  OutputStringSlice := make([]string, 0, rows * cols)

  for i := 0; i < rows; i++ {
      ss := strconv.FormatFloat(M.At(i, 0), 'G', places, 64)
      OutputStringSlice = append(OutputStringSlice, fmt.Sprintf("%10s", ss))
  } // END FOR i
  OutputStringSlice = append(OutputStringSlice, "\n")
  return OutputStringSlice
} // END write
<commit_msg>07/31/2020 07:08:14 PM gonumsolve/gonumsolve.go -- works as intended.<commit_after>package main

/*
MODULE Solve;

  REVISION
  --------
   2 Mar 05 -- Added prompts to remind me of the file format.
   3 Mar 05 -- Made version 2 write lines like eqn w/o =.
   4 Mar 05 -- Don't need N as 1st line now.
  26 Feb 06 -- Will reject non-numeric entries and allows <tab> as delim.
  24 Dec 16 -- Converted to Go.
  31 Jul 20 -- Starting conversion to use gonum.org/mat code
*/

import (
	"bufio"
	"fmt"
	"getcommandline"
	"gonum.org/v1/gonum/mat" // from memory.  This needs to be confirmed.
	"io"
	mymat "mat"
	"math"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"tokenize"
)

const LastCompiled = "July 31, 2020"
const MaxN = 30
const toleranceFactor = 1e-6

// IM (inputMatrix) is not square because the B column vector is in last column of IM.
//                                        was TYPE MaxRealArray = ARRAY [1..MaxN],[1..MaxN+1] OF LONGREAL; in M-2 code.

// type Matrix2D [][]float64;  not needed here.  It's internal to mymat package.

func main() {
	/* ------------------------------------------------------------------------
	                           MAIN PROGRAM
	------------------------------------------------------------------------*/

	fmt.Println(" Equation solver written in Go.  Last compiled ", LastCompiled)
	fmt.Println()

	if len(os.Args) <= 1 {
		fmt.Println(" Usage: solve <filename>")
		fmt.Println(" Solves vector equation A * X = B; and A is a square coef matrix.")
		fmt.Println(" N is determined by number of rows and B value is last on each line.")
		os.Exit(0)
	}

	commandline := getcommandline.GetCommandLineString()
	cleancommandline := filepath.Clean(commandline)
	fmt.Println(" filename on command line is ", cleancommandline)

	//  cleancommandline = "xy1.txt";

	infile, err := os.Open(cleancommandline)
	if err != nil {
		fmt.Println(" Cannot open input file.  Does it exist?  Error is ", err)
		os.Exit(1)
	}

	defer infile.Close()
	scanner := bufio.NewScanner(infile)

	IM := mymat.NewMatrix(MaxN, MaxN+1) // IM is input matrix
	IM = mymat.Zero(IM)

	lines := 0
CountLinesLoop:
	for { // read, count and process lines
		for n := 0; n < MaxN; n++ {
			readSuccess := scanner.Scan()
			if readSuccess {
				// Non-numeric tokens are comments, and lines not beginning with a number are comment lines.
			} else {
				break CountLinesLoop // break outer loop
			} // if readSuccess

			inputline := scanner.Text()
			if readErr := scanner.Err(); readErr != nil {
				if readErr == io.EOF {
					break CountLinesLoop // break outer loop
				} else { // this may be redundant because of the readSuccess test
					break CountLinesLoop // break outer loop
				}
			}

			tokenize.INITKN(inputline)
			col := 0
			EOL := false
			for (EOL == false) && (n <= MaxN) { // read numbers into IM input matrix.
				token, EOL := tokenize.GETTKNREAL()
				if EOL {
					break
				}
				if token.State != tokenize.DGT {
					break
				} // treat remainder of line as a comment
				IM[lines][col] = token.Rsum // remember that IM is Input Matrix
				col++
			}
			if col > 0 { // allow comments by incrementing the pointer only when have numbers on the line.
				lines++
			}
		} // END for n
	} // END outer reading loop

	N := lines // Note: lines is 0 origin, and therefore so is N

	// Now need to create A and B matrices

	A := mymat.NewMatrix(N, N)
	B := mymat.NewMatrix(N, 1)
	for row := range A {
		for col := range A[0] {
			A[row][col] = IM[row][col]
		} // END FOR col
		B[row][0] = IM[row][N] // I have to keep remembering that [0,0] is the first row and col.
	} // END FOR row

	fmt.Println(" coef matrix A is:")
	ss := mymat.Write(A, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	fmt.Println(" Right hand side vector matrix B is:")
	ss = mymat.Write(B, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	ans := mymat.NewMatrix(N, N)
	ans = mymat.Solve(A, B) // Solve (ra1, ra2, ans, N, 1);
	fmt.Println("The solution X to AX = B using Solve is")
	ss = mymat.Write(ans, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	ans2 := mymat.NewMatrix(N, N)
	ans2 = mymat.GaussJ(A, B) // Solve (ra1, ra2, ans, N, 1);
	fmt.Println("The solution X to AX = B using GaussJ is")
	ss = mymat.Write(ans2, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	pause()

	// Check that the solution looks right.

	C := mymat.NewMatrix(N, 1)
	D := mymat.NewMatrix(N, 1)
	C = mymat.Mul(A, ans)
	D = mymat.Sub(B, C)

	fmt.Println("As a check, AX-B should be 0, and evaluates to")
	ss = mymat.Write(D, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	D = mymat.BelowSmallMakeZero(D)

	fmt.Println("As a check, AX-B should be all zeros after calling BelowSmall.  It evaluates to")
	ss = mymat.Write(D, 5)
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()
	fmt.Println()

	pause()

	// Now for gonum.org mat code

	fmt.Println(" Using gonum.org Solve")
	newA := mat.NewDense(N, N, nil)
	newB := mat.NewDense(N, 1, nil)
	for row := 0; row < N; row++ {
		for col := 0; col < N; col++ {
			newA.Set(row, col, A[row][col])
		} // END FOR col
		newB.Set(row, 0, B[row][0])
	} // END FOR row

	var newX mat.Dense // construct empty matrix
	mat.NewDense(N, N, nil)
	er := newX.Solve(newA, newB)
	if er != nil {
		fmt.Println(" Error from newX.Solve is", er)
	}
	ss = write(newX, 5)
	fmt.Println(" write newX:")
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	ss = writeZero(newX, 5)
	fmt.Println(" writeZero newX:")
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()

	fmt.Println()

	pause()

	// Now to make B a vector (well, a column vector, but still a vector) and then use vector routines.  Just to see if I can get it to work, too.

	vecB := mat.NewVecDense(N, nil)
	for i := 0; i < N; i++ {
		vecB.SetVec(i, newB.At(i, 0))
	}

	var vecX mat.VecDense
	er = vecX.SolveVec(newA, vecB)
	if er != nil {
		fmt.Println(" Error from vecX.Solve is", er)
	}

	fmt.Println(" B and X are now vectors instead of matricies")
	ss = vectorWrite(*vecB, 5)
	fmt.Println(" B: ")
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()
	fmt.Println()

	ss = vectorWrite(vecX, 5)
	fmt.Println(" X: ")
	for _, s := range ss {
		fmt.Print(s)
	}
	fmt.Println()
	fmt.Println()

	var inverseA mat.Dense
	err = inverseA.Inverse(newA)
	if err != nil {
		fmt.Println(" Error from inverseA.Inverse is", err)
	}
	var newX2 mat.Dense // empty matrix is constructed
	newX2.Mul(&inverseA, newB)
	fmt.Println(" Using inverse of A to solve for X:")
	ss = write(newX2, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	fmt.Println()
	fmt.Println()

	pause()

	fmt.Println(" Do the gonum.org computations look right?  First newA * newX - newB by solve, then by inverse")

	var newE, newF mat.Dense
	newE.Mul(newA, &newX) // compute newE = newA * newX
	newE.Sub(newB, &newE) // compute newA * newX - newB which should be all zeros
	fmt.Println(" newA * newX - newB should be all zeros")
	ss = write(newE, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	fmt.Println(" should still be all zeros")
	ss = writeZero(newE, 5)
	for _, s := range ss {
		fmt.Print(s)
	}

	fmt.Println()
	fmt.Println()

	pause()

	newF.Mul(newA, &newX2) // compute newF = NewA * newX2
	newF.Sub(newB, &newF)  // compute newA * newX2 - newB which should also be all zeros
	ss = write(newF, 5)
	fmt.Println(" newA * newX2 - newB should be matrix of all zeros")
	for _, s := range ss {
		fmt.Print(s)
	}

	ss = writeZero(newF, 5)
	fmt.Println(" should still be matrix of all zeros")
	for _, s := range ss {
		fmt.Print(s)
	}

	fmt.Println()
	fmt.Println()

	pause()

	fmt.Println(" newA * vecX - vecB should be all zeros")
	var checkVec mat.VecDense
	checkVec.MulVec(newA, &vecX)
	checkVec.SubVec(vecB, &checkVec)

    for _, s := range ss {
      fmt.Print(s)
    }

    fmt.Println()
    fmt.Println()

} // END Solve.

func pause() {
	scnr := bufio.NewScanner(os.Stdin)
	fmt.Print(" pausing ... hit <enter>")
	scnr.Scan()
	answer := scnr.Text()
	if err := scnr.Err(); err != nil {
		fmt.Fprintln(os.Stderr, "reading standard input:", err)
		os.Exit(1)
	}
	ans := strings.TrimSpace(answer)
	ans = strings.ToUpper(ans)
	fmt.Println(ans)
}

func write(M mat.Dense, places int) []string {

	// Writes the r x c matrix M to a string slice which is returned.  Each string represents a field "places" characters wide.

	rows, cols := M.Dims()
	OutputStringSlice := make([]string, 0, rows*cols)

	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			ss := strconv.FormatFloat(M.At(i, j), 'G', places, 64)
			OutputStringSlice = append(OutputStringSlice, fmt.Sprintf("%10s", ss))
		} // END FOR j
		OutputStringSlice = append(OutputStringSlice, "\n")
	} // END FOR i
	OutputStringSlice = append(OutputStringSlice, "\n")
	return OutputStringSlice
} // END write

func writeZero(M mat.Dense, places int) []string {

	// Writes the r x c matrix M to a string slice which is returned.  Each string represents a field "places" characters wide.

	rows, cols := M.Dims()
	OutputStringSlice := make([]string, 0, rows*cols)

	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			v := M.At(i, j)
			ss := ""
			if math.Abs(v) < toleranceFactor {
				ss = "0    "
			} else {
				ss = strconv.FormatFloat(M.At(i, j), 'G', places, 64)
			}
			OutputStringSlice = append(OutputStringSlice, fmt.Sprintf("%10s", ss))
		} // END FOR j
		OutputStringSlice = append(OutputStringSlice, "\n")
	} // END FOR i
	OutputStringSlice = append(OutputStringSlice, "\n")
	return OutputStringSlice
} // END write

func vectorWrite(M mat.VecDense, places int) []string {
	rows, cols := M.Dims()
	OutputStringSlice := make([]string, 0, rows*cols)

	for i := 0; i < rows; i++ {
		ss := strconv.FormatFloat(M.At(i, 0), 'G', places, 64)
		OutputStringSlice = append(OutputStringSlice, fmt.Sprintf("%10s", ss))
	} // END FOR i
	OutputStringSlice = append(OutputStringSlice, "\n")
	return OutputStringSlice
} // END write
<|endoftext|>


<commit_before>/*
	Utility functions used by all of ctfe.
*/

package util

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"text/template"

	"go.skia.org/infra/ct/go/util"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/login"
)

// URIs for frontend handlers.
const (
	CHROMIUM_PERF_URI                  = "chromium_perf/"
	CHROMIUM_PERF_RUNS_URI             = "chromium_perf_runs/"
	GET_CHROMIUM_PERF_RUN_STATUS_URI   = "get_chromium_perf_run_status"
	ADD_CHROMIUM_PERF_TASK_POST_URI    = "_/add_chromium_perf_task"
	GET_CHROMIUM_PERF_TASKS_POST_URI   = "_/get_chromium_perf_tasks"
	UPDATE_CHROMIUM_PERF_TASK_POST_URI = "_/update_chromium_perf_task"
	DELETE_CHROMIUM_PERF_TASK_POST_URI = "_/delete_chromium_perf_task"
	REDO_CHROMIUM_PERF_TASK_POST_URI   = "_/redo_chromium_perf_task"

	CHROMIUM_ANALYSIS_URI                  = "chromium_analysis/"
	CHROMIUM_ANALYSIS_RUNS_URI             = "chromium_analysis_runs/"
	ADD_CHROMIUM_ANALYSIS_TASK_POST_URI    = "_/add_chromium_analysis_task"
	GET_CHROMIUM_ANALYSIS_TASKS_POST_URI   = "_/get_chromium_analysis_tasks"
	UPDATE_CHROMIUM_ANALYSIS_TASK_POST_URI = "_/update_chromium_analysis_task"
	DELETE_CHROMIUM_ANALYSIS_TASK_POST_URI = "_/delete_chromium_analysis_task"
	REDO_CHROMIUM_ANALYSIS_TASK_POST_URI   = "_/redo_chromium_analysis_task"

	CAPTURE_SKPS_URI                  = "capture_skps/"
	CAPTURE_SKPS_RUNS_URI             = "capture_skp_runs/"
	ADD_CAPTURE_SKPS_TASK_POST_URI    = "_/add_capture_skps_task"
	GET_CAPTURE_SKPS_TASKS_POST_URI   = "_/get_capture_skp_tasks"
	UPDATE_CAPTURE_SKPS_TASK_POST_URI = "_/update_capture_skps_task"
	DELETE_CAPTURE_SKPS_TASK_POST_URI = "_/delete_capture_skps_task"
	REDO_CAPTURE_SKPS_TASK_POST_URI   = "_/redo_capture_skps_task"

	LUA_SCRIPT_URI                  = "lua_script/"
	LUA_SCRIPT_RUNS_URI             = "lua_script_runs/"
	ADD_LUA_SCRIPT_TASK_POST_URI    = "_/add_lua_script_task"
	GET_LUA_SCRIPT_TASKS_POST_URI   = "_/get_lua_script_tasks"
	UPDATE_LUA_SCRIPT_TASK_POST_URI = "_/update_lua_script_task"
	DELETE_LUA_SCRIPT_TASK_POST_URI = "_/delete_lua_script_task"
	REDO_LUA_SCRIPT_TASK_POST_URI   = "_/redo_lua_script_task"

	CHROMIUM_BUILD_URI                  = "chromium_builds/"
	CHROMIUM_BUILD_RUNS_URI             = "chromium_builds_runs/"
	CHROMIUM_REV_DATA_POST_URI          = "_/chromium_rev_data"
	SKIA_REV_DATA_POST_URI              = "_/skia_rev_data"
	ADD_CHROMIUM_BUILD_TASK_POST_URI    = "_/add_chromium_build_task"
	GET_CHROMIUM_BUILD_TASKS_POST_URI   = "_/get_chromium_build_tasks"
	UPDATE_CHROMIUM_BUILD_TASK_POST_URI = "_/update_chromium_build_task"
	DELETE_CHROMIUM_BUILD_TASK_POST_URI = "_/delete_chromium_build_task"
	REDO_CHROMIUM_BUILD_TASK_POST_URI   = "_/redo_chromium_build_task"

	ADMIN_TASK_URI = "admin_tasks/"

	RECREATE_PAGE_SETS_RUNS_URI             = "recreate_page_sets_runs/"
	ADD_RECREATE_PAGE_SETS_TASK_POST_URI    = "_/add_recreate_page_sets_task"
	GET_RECREATE_PAGE_SETS_TASKS_POST_URI   = "_/get_recreate_page_sets_tasks"
	UPDATE_RECREATE_PAGE_SETS_TASK_POST_URI = "_/update_recreate_page_sets_task"
	DELETE_RECREATE_PAGE_SETS_TASK_POST_URI = "_/delete_recreate_page_sets_task"
	REDO_RECREATE_PAGE_SETS_TASK_POST_URI   = "_/redo_recreate_page_sets_task"

	RECREATE_WEBPAGE_ARCHIVES_RUNS_URI             = "recreate_webpage_archives_runs/"
	ADD_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI    = "_/add_recreate_webpage_archives_task"
	GET_RECREATE_WEBPAGE_ARCHIVES_TASKS_POST_URI   = "_/get_recreate_webpage_archives_tasks"
	UPDATE_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI = "_/update_recreate_webpage_archives_task"
	DELETE_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI = "_/delete_recreate_webpage_archives_task"
	REDO_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI   = "_/redo_recreate_webpage_archives_task"

	RUNS_HISTORY_URI = "history/"

	PENDING_TASKS_URI           = "queue/"
	GET_OLDEST_PENDING_TASK_URI = "_/get_oldest_pending_task"

	PAGE_SETS_PARAMETERS_POST_URI = "_/page_sets/"
	CL_DATA_POST_URI              = "_/cl_data"
	BENCHMARKS_PLATFORMS_POST_URI = "_/benchmarks_platforms/"
)

// Function to run before executing a template.
var PreExecuteTemplateHook = func() {}

func UserHasEditRights(r *http.Request) bool {
	return strings.HasSuffix(login.LoggedInAs(r), "@google.com") || strings.HasSuffix(login.LoggedInAs(r), "@chromium.org")
}

func UserHasAdminRights(r *http.Request) bool {
	return UserHasEditRights(r) && login.IsAdmin(r)
}

func ExecuteSimpleTemplate(template *template.Template, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")

	PreExecuteTemplateHook()
	if err := template.Execute(w, struct{}{}); err != nil {
		httputils.ReportError(w, r, err, fmt.Sprintf("Failed to expand template: %v", err))
		return
	}
}

type LengthCheck struct {
	Name  string
	Value string
	Limit int64
}

func CheckLengths(checks []LengthCheck) error {
	for _, check := range checks {
		if int64(len(check.Value)) > check.Limit {
			return fmt.Errorf("Value of %s is too long; limit %d bytes", check.Name, check.Limit)
		}
	}
	return nil
}

func GetQualifiedCustomWebpages(customWebpages, benchmarkArgs string) ([]string, error) {
	qualifiedWebpages := []string{}
	if customWebpages != "" {
		if !strings.Contains(benchmarkArgs, util.USE_LIVE_SITES_FLAGS) {
			return nil, errors.New("Cannot use custom webpages without " + util.USE_LIVE_SITES_FLAGS)
		}
		r := csv.NewReader(strings.NewReader(customWebpages))
		for {
			records, err := r.Read()
			if err == io.EOF {
				break
			}
			if err != nil {
				return nil, err
			}
			for _, record := range records {
				if strings.TrimSpace(record) == "" {
					return nil, errors.New("Cannot specify empty webpages")
				}
				var qualifiedWebpage string
				if strings.HasPrefix(record, "http://") || strings.HasPrefix(record, "https://") {
					qualifiedWebpage = record
				} else if len(strings.Split(record, ".")) > 2 {
					qualifiedWebpage = fmt.Sprintf("http://%s", record)
				} else {
					qualifiedWebpage = fmt.Sprintf("http://www.%s", record)
				}
				qualifiedWebpages = append(qualifiedWebpages, qualifiedWebpage)
			}
		}
	}
	return qualifiedWebpages, nil
}
<commit_msg>[CTFE] Skip empty custom webpages instead of reporting error<commit_after>/*
	Utility functions used by all of ctfe.
*/

package util

import (
	"encoding/csv"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"
	"text/template"

	"go.skia.org/infra/ct/go/util"
	"go.skia.org/infra/go/httputils"
	"go.skia.org/infra/go/login"
)

// URIs for frontend handlers.
const (
	CHROMIUM_PERF_URI                  = "chromium_perf/"
	CHROMIUM_PERF_RUNS_URI             = "chromium_perf_runs/"
	GET_CHROMIUM_PERF_RUN_STATUS_URI   = "get_chromium_perf_run_status"
	ADD_CHROMIUM_PERF_TASK_POST_URI    = "_/add_chromium_perf_task"
	GET_CHROMIUM_PERF_TASKS_POST_URI   = "_/get_chromium_perf_tasks"
	UPDATE_CHROMIUM_PERF_TASK_POST_URI = "_/update_chromium_perf_task"
	DELETE_CHROMIUM_PERF_TASK_POST_URI = "_/delete_chromium_perf_task"
	REDO_CHROMIUM_PERF_TASK_POST_URI   = "_/redo_chromium_perf_task"

	CHROMIUM_ANALYSIS_URI                  = "chromium_analysis/"
	CHROMIUM_ANALYSIS_RUNS_URI             = "chromium_analysis_runs/"
	ADD_CHROMIUM_ANALYSIS_TASK_POST_URI    = "_/add_chromium_analysis_task"
	GET_CHROMIUM_ANALYSIS_TASKS_POST_URI   = "_/get_chromium_analysis_tasks"
	UPDATE_CHROMIUM_ANALYSIS_TASK_POST_URI = "_/update_chromium_analysis_task"
	DELETE_CHROMIUM_ANALYSIS_TASK_POST_URI = "_/delete_chromium_analysis_task"
	REDO_CHROMIUM_ANALYSIS_TASK_POST_URI   = "_/redo_chromium_analysis_task"

	CAPTURE_SKPS_URI                  = "capture_skps/"
	CAPTURE_SKPS_RUNS_URI             = "capture_skp_runs/"
	ADD_CAPTURE_SKPS_TASK_POST_URI    = "_/add_capture_skps_task"
	GET_CAPTURE_SKPS_TASKS_POST_URI   = "_/get_capture_skp_tasks"
	UPDATE_CAPTURE_SKPS_TASK_POST_URI = "_/update_capture_skps_task"
	DELETE_CAPTURE_SKPS_TASK_POST_URI = "_/delete_capture_skps_task"
	REDO_CAPTURE_SKPS_TASK_POST_URI   = "_/redo_capture_skps_task"

	LUA_SCRIPT_URI                  = "lua_script/"
	LUA_SCRIPT_RUNS_URI             = "lua_script_runs/"
	ADD_LUA_SCRIPT_TASK_POST_URI    = "_/add_lua_script_task"
	GET_LUA_SCRIPT_TASKS_POST_URI   = "_/get_lua_script_tasks"
	UPDATE_LUA_SCRIPT_TASK_POST_URI = "_/update_lua_script_task"
	DELETE_LUA_SCRIPT_TASK_POST_URI = "_/delete_lua_script_task"
	REDO_LUA_SCRIPT_TASK_POST_URI   = "_/redo_lua_script_task"

	CHROMIUM_BUILD_URI                  = "chromium_builds/"
	CHROMIUM_BUILD_RUNS_URI             = "chromium_builds_runs/"
	CHROMIUM_REV_DATA_POST_URI          = "_/chromium_rev_data"
	SKIA_REV_DATA_POST_URI              = "_/skia_rev_data"
	ADD_CHROMIUM_BUILD_TASK_POST_URI    = "_/add_chromium_build_task"
	GET_CHROMIUM_BUILD_TASKS_POST_URI   = "_/get_chromium_build_tasks"
	UPDATE_CHROMIUM_BUILD_TASK_POST_URI = "_/update_chromium_build_task"
	DELETE_CHROMIUM_BUILD_TASK_POST_URI = "_/delete_chromium_build_task"
	REDO_CHROMIUM_BUILD_TASK_POST_URI   = "_/redo_chromium_build_task"

	ADMIN_TASK_URI = "admin_tasks/"

	RECREATE_PAGE_SETS_RUNS_URI             = "recreate_page_sets_runs/"
	ADD_RECREATE_PAGE_SETS_TASK_POST_URI    = "_/add_recreate_page_sets_task"
	GET_RECREATE_PAGE_SETS_TASKS_POST_URI   = "_/get_recreate_page_sets_tasks"
	UPDATE_RECREATE_PAGE_SETS_TASK_POST_URI = "_/update_recreate_page_sets_task"
	DELETE_RECREATE_PAGE_SETS_TASK_POST_URI = "_/delete_recreate_page_sets_task"
	REDO_RECREATE_PAGE_SETS_TASK_POST_URI   = "_/redo_recreate_page_sets_task"

	RECREATE_WEBPAGE_ARCHIVES_RUNS_URI             = "recreate_webpage_archives_runs/"
	ADD_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI    = "_/add_recreate_webpage_archives_task"
	GET_RECREATE_WEBPAGE_ARCHIVES_TASKS_POST_URI   = "_/get_recreate_webpage_archives_tasks"
	UPDATE_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI = "_/update_recreate_webpage_archives_task"
	DELETE_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI = "_/delete_recreate_webpage_archives_task"
	REDO_RECREATE_WEBPAGE_ARCHIVES_TASK_POST_URI   = "_/redo_recreate_webpage_archives_task"

	RUNS_HISTORY_URI = "history/"

	PENDING_TASKS_URI           = "queue/"
	GET_OLDEST_PENDING_TASK_URI = "_/get_oldest_pending_task"

	PAGE_SETS_PARAMETERS_POST_URI = "_/page_sets/"
	CL_DATA_POST_URI              = "_/cl_data"
	BENCHMARKS_PLATFORMS_POST_URI = "_/benchmarks_platforms/"
)

// Function to run before executing a template.
var PreExecuteTemplateHook = func() {}

func UserHasEditRights(r *http.Request) bool {
	return strings.HasSuffix(login.LoggedInAs(r), "@google.com") || strings.HasSuffix(login.LoggedInAs(r), "@chromium.org")
}

func UserHasAdminRights(r *http.Request) bool {
	return UserHasEditRights(r) && login.IsAdmin(r)
}

func ExecuteSimpleTemplate(template *template.Template, w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")

	PreExecuteTemplateHook()
	if err := template.Execute(w, struct{}{}); err != nil {
		httputils.ReportError(w, r, err, fmt.Sprintf("Failed to expand template: %v", err))
		return
	}
}

type LengthCheck struct {
	Name  string
	Value string
	Limit int64
}

func CheckLengths(checks []LengthCheck) error {
	for _, check := range checks {
		if int64(len(check.Value)) > check.Limit {
			return fmt.Errorf("Value of %s is too long; limit %d bytes", check.Name, check.Limit)
		}
	}
	return nil
}

func GetQualifiedCustomWebpages(customWebpages, benchmarkArgs string) ([]string, error) {
	qualifiedWebpages := []string{}
	if customWebpages != "" {
		if !strings.Contains(benchmarkArgs, util.USE_LIVE_SITES_FLAGS) {
			return nil, errors.New("Cannot use custom webpages without " + util.USE_LIVE_SITES_FLAGS)
		}
		r := csv.NewReader(strings.NewReader(customWebpages))
		for {
			records, err := r.Read()
			if err == io.EOF {
				break
			}
			if err != nil {
				return nil, err
			}
			for _, record := range records {
				if strings.TrimSpace(record) == "" {
					// Skip empty webpages.
					continue
				}
				var qualifiedWebpage string
				if strings.HasPrefix(record, "http://") || strings.HasPrefix(record, "https://") {
					qualifiedWebpage = record
				} else if len(strings.Split(record, ".")) > 2 {
					qualifiedWebpage = fmt.Sprintf("http://%s", record)
				} else {
					qualifiedWebpage = fmt.Sprintf("http://www.%s", record)
				}
				qualifiedWebpages = append(qualifiedWebpages, qualifiedWebpage)
			}
		}
	}
	return qualifiedWebpages, nil
}
<|endoftext|>


<commit_before>package engine

import (
	"fmt"

	"github.com/NetSys/quilt/cluster/provider"
	"github.com/NetSys/quilt/db"
	"github.com/NetSys/quilt/join"
	"github.com/NetSys/quilt/stitch"
	"github.com/NetSys/quilt/util"

	log "github.com/Sirupsen/logrus"
)

var myIP = util.MyIP
var defaultDiskSize = 32

// UpdatePolicy executes transactions on 'conn' to make it reflect a new policy,
// 'stitch'.
func UpdatePolicy(conn db.Conn, stitch stitch.Stitch) error {
	txn := func(db db.Database) error {
		return updateTxn(db, stitch)
	}

	if err := conn.Transact(txn); err != nil {
		return err
	}

	return nil
}

func updateTxn(view db.Database, stitch stitch.Stitch) error {
	if err := clusterTxn(view, stitch); err != nil {
		return err
	}

	if err := machineTxn(view, stitch); err != nil {
		return err
	}

	if err := aclTxn(view, stitch); err != nil {
		return err
	}

	return nil
}

func clusterTxn(view db.Database, stitch stitch.Stitch) error {
	namespace := stitch.Namespace
	if namespace == "" {
		namespace = "default-namespace"
		msg := "policy did not specify 'Namespace', defaulting to '%s'"
		log.Warn(fmt.Sprintf(msg, namespace))
	}

	cluster, err := view.GetCluster()
	if err != nil {
		cluster = view.InsertCluster()
	}

	cluster.Namespace = namespace
	cluster.Spec = stitch.String()
	view.Commit(cluster)
	return nil
}

func aclTxn(view db.Database, specHandle stitch.Stitch) error {
	aclRow, err := view.GetACL()
	if err != nil {
		aclRow = view.InsertACL()
	}

	aclRow.Admin = resolveACLs(specHandle.AdminACL)

	var applicationPorts []db.PortRange
	for _, conn := range specHandle.Connections {
		if conn.From == stitch.PublicInternetLabel {
			applicationPorts = append(applicationPorts, db.PortRange{
				MinPort: conn.MinPort,
				MaxPort: conn.MaxPort,
			})
		}
	}
	aclRow.ApplicationPorts = applicationPorts

	view.Commit(aclRow)
	return nil
}

// toDBMachine converts machines specified in the Stitch into db.Machines that can
// be compared against what's already in the db.
// Specifically, it sets the role of the db.Machine, the size (which may depend
// on RAM and CPU constraints), and the provider.
// Additionally, it skips machines with invalid roles, sizes or providers.
func toDBMachine(machines []stitch.Machine, maxPrice float64) []db.Machine {
	var hasMaster, hasWorker bool
	var dbMachines []db.Machine
	for _, stitchm := range machines {
		var m db.Machine

		role, err := db.ParseRole(stitchm.Role)
		if err != nil {
			log.WithError(err).Error("Error parsing role.")
			continue
		}
		m.Role = role

		hasMaster = hasMaster || role == db.Master
		hasWorker = hasWorker || role == db.Worker

		p, err := db.ParseProvider(stitchm.Provider)
		if err != nil {
			log.WithError(err).Error("Error parsing provider.")
			continue
		}
		m.Provider = p
		m.Size = stitchm.Size

		if m.Size == "" {
			providerInst := provider.New(p)
			m.Size = providerInst.ChooseSize(
				stitchm.RAM, stitchm.CPU, maxPrice)
			if m.Size == "" {
				log.Errorf("No valid size for %v, skipping.", m)
				continue
			}
		}

		m.DiskSize = stitchm.DiskSize
		if m.DiskSize == 0 {
			m.DiskSize = defaultDiskSize
		}

		m.SSHKeys = stitchm.SSHKeys
		m.Region = stitchm.Region
		dbMachines = append(dbMachines, provider.DefaultRegion(m))
	}

	if hasMaster && !hasWorker {
		log.Warning("A Master was specified but no workers.")
		return nil
	} else if hasWorker && !hasMaster {
		log.Warning("A Worker was specified but no masters.")
		return nil
	}

	return dbMachines
}

func machineTxn(view db.Database, stitch stitch.Stitch) error {
	// XXX: How best to deal with machines that don't specify enough information?
	maxPrice := stitch.MaxPrice
	stitchMachines := toDBMachine(stitch.Machines, maxPrice)

	dbMachines := view.SelectFromMachine(nil)

	scoreFun := func(left, right interface{}) int {
		stitchMachine := left.(db.Machine)
		dbMachine := right.(db.Machine)

		switch {
		case dbMachine.Provider != stitchMachine.Provider:
			return -1
		case dbMachine.Region != stitchMachine.Region:
			return -1
		case dbMachine.Size != "" && stitchMachine.Size != dbMachine.Size:
			return -1
		case dbMachine.Role != db.None && dbMachine.Role != stitchMachine.Role:
			return -1
		case dbMachine.DiskSize != stitchMachine.DiskSize:
			return -1
		case dbMachine.PrivateIP == "":
			return 2
		case dbMachine.PublicIP == "":
			return 1
		default:
			return 0
		}
	}

	pairs, bootList, terminateList := join.Join(stitchMachines, dbMachines, scoreFun)

	for _, toTerminate := range terminateList {
		toTerminate := toTerminate.(db.Machine)
		view.Remove(toTerminate)
	}

	for _, bootSet := range bootList {
		bootSet := bootSet.(db.Machine)

		pairs = append(pairs, join.Pair{L: bootSet, R: view.InsertMachine()})
	}

	for _, pair := range pairs {
		stitchMachine := pair.L.(db.Machine)
		dbMachine := pair.R.(db.Machine)

		dbMachine.Role = stitchMachine.Role
		dbMachine.Size = stitchMachine.Size
		dbMachine.DiskSize = stitchMachine.DiskSize
		dbMachine.Provider = stitchMachine.Provider
		dbMachine.Region = stitchMachine.Region
		dbMachine.SSHKeys = stitchMachine.SSHKeys
		view.Commit(dbMachine)
	}

	return nil
}

func resolveACLs(acls []string) []string {
	var result []string
	for _, acl := range acls {
		if acl == "local" {
			ip, err := myIP()
			if err != nil {
				log.WithError(err).Warn("Failed to get IP address.")
				continue
			}
			acl = ip + "/32"
		}
		result = append(result, acl)
	}

	return result
}
<commit_msg>engine: Remove warning about using default namespace<commit_after>package engine

import (
	"github.com/NetSys/quilt/cluster/provider"
	"github.com/NetSys/quilt/db"
	"github.com/NetSys/quilt/join"
	"github.com/NetSys/quilt/stitch"
	"github.com/NetSys/quilt/util"

	log "github.com/Sirupsen/logrus"
)

var myIP = util.MyIP
var defaultDiskSize = 32

// UpdatePolicy executes transactions on 'conn' to make it reflect a new policy,
// 'stitch'.
func UpdatePolicy(conn db.Conn, stitch stitch.Stitch) error {
	txn := func(db db.Database) error {
		return updateTxn(db, stitch)
	}

	if err := conn.Transact(txn); err != nil {
		return err
	}

	return nil
}

func updateTxn(view db.Database, stitch stitch.Stitch) error {
	if err := clusterTxn(view, stitch); err != nil {
		return err
	}

	if err := machineTxn(view, stitch); err != nil {
		return err
	}

	if err := aclTxn(view, stitch); err != nil {
		return err
	}

	return nil
}

func clusterTxn(view db.Database, stitch stitch.Stitch) error {
	namespace := stitch.Namespace
	if namespace == "" {
		namespace = "default-namespace"
	}

	cluster, err := view.GetCluster()
	if err != nil {
		cluster = view.InsertCluster()
	}

	cluster.Namespace = namespace
	cluster.Spec = stitch.String()
	view.Commit(cluster)
	return nil
}

func aclTxn(view db.Database, specHandle stitch.Stitch) error {
	aclRow, err := view.GetACL()
	if err != nil {
		aclRow = view.InsertACL()
	}

	aclRow.Admin = resolveACLs(specHandle.AdminACL)

	var applicationPorts []db.PortRange
	for _, conn := range specHandle.Connections {
		if conn.From == stitch.PublicInternetLabel {
			applicationPorts = append(applicationPorts, db.PortRange{
				MinPort: conn.MinPort,
				MaxPort: conn.MaxPort,
			})
		}
	}
	aclRow.ApplicationPorts = applicationPorts

	view.Commit(aclRow)
	return nil
}

// toDBMachine converts machines specified in the Stitch into db.Machines that can
// be compared against what's already in the db.
// Specifically, it sets the role of the db.Machine, the size (which may depend
// on RAM and CPU constraints), and the provider.
// Additionally, it skips machines with invalid roles, sizes or providers.
func toDBMachine(machines []stitch.Machine, maxPrice float64) []db.Machine {
	var hasMaster, hasWorker bool
	var dbMachines []db.Machine
	for _, stitchm := range machines {
		var m db.Machine

		role, err := db.ParseRole(stitchm.Role)
		if err != nil {
			log.WithError(err).Error("Error parsing role.")
			continue
		}
		m.Role = role

		hasMaster = hasMaster || role == db.Master
		hasWorker = hasWorker || role == db.Worker

		p, err := db.ParseProvider(stitchm.Provider)
		if err != nil {
			log.WithError(err).Error("Error parsing provider.")
			continue
		}
		m.Provider = p
		m.Size = stitchm.Size

		if m.Size == "" {
			providerInst := provider.New(p)
			m.Size = providerInst.ChooseSize(
				stitchm.RAM, stitchm.CPU, maxPrice)
			if m.Size == "" {
				log.Errorf("No valid size for %v, skipping.", m)
				continue
			}
		}

		m.DiskSize = stitchm.DiskSize
		if m.DiskSize == 0 {
			m.DiskSize = defaultDiskSize
		}

		m.SSHKeys = stitchm.SSHKeys
		m.Region = stitchm.Region
		dbMachines = append(dbMachines, provider.DefaultRegion(m))
	}

	if hasMaster && !hasWorker {
		log.Warning("A Master was specified but no workers.")
		return nil
	} else if hasWorker && !hasMaster {
		log.Warning("A Worker was specified but no masters.")
		return nil
	}

	return dbMachines
}

func machineTxn(view db.Database, stitch stitch.Stitch) error {
	// XXX: How best to deal with machines that don't specify enough information?
	maxPrice := stitch.MaxPrice
	stitchMachines := toDBMachine(stitch.Machines, maxPrice)

	dbMachines := view.SelectFromMachine(nil)

	scoreFun := func(left, right interface{}) int {
		stitchMachine := left.(db.Machine)
		dbMachine := right.(db.Machine)

		switch {
		case dbMachine.Provider != stitchMachine.Provider:
			return -1
		case dbMachine.Region != stitchMachine.Region:
			return -1
		case dbMachine.Size != "" && stitchMachine.Size != dbMachine.Size:
			return -1
		case dbMachine.Role != db.None && dbMachine.Role != stitchMachine.Role:
			return -1
		case dbMachine.DiskSize != stitchMachine.DiskSize:
			return -1
		case dbMachine.PrivateIP == "":
			return 2
		case dbMachine.PublicIP == "":
			return 1
		default:
			return 0
		}
	}

	pairs, bootList, terminateList := join.Join(stitchMachines, dbMachines, scoreFun)

	for _, toTerminate := range terminateList {
		toTerminate := toTerminate.(db.Machine)
		view.Remove(toTerminate)
	}

	for _, bootSet := range bootList {
		bootSet := bootSet.(db.Machine)

		pairs = append(pairs, join.Pair{L: bootSet, R: view.InsertMachine()})
	}

	for _, pair := range pairs {
		stitchMachine := pair.L.(db.Machine)
		dbMachine := pair.R.(db.Machine)

		dbMachine.Role = stitchMachine.Role
		dbMachine.Size = stitchMachine.Size
		dbMachine.DiskSize = stitchMachine.DiskSize
		dbMachine.Provider = stitchMachine.Provider
		dbMachine.Region = stitchMachine.Region
		dbMachine.SSHKeys = stitchMachine.SSHKeys
		view.Commit(dbMachine)
	}

	return nil
}

func resolveACLs(acls []string) []string {
	var result []string
	for _, acl := range acls {
		if acl == "local" {
			ip, err := myIP()
			if err != nil {
				log.WithError(err).Warn("Failed to get IP address.")
				continue
			}
			acl = ip + "/32"
		}
		result = append(result, acl)
	}

	return result
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"strings"
)

func (self *Generator) GenEnums(ns *Namespace) {
	self.genEnums(ns.Enums, "enums")
}

func (self *Generator) GenFlags(ns *Namespace) {
	self.genEnums(ns.Bitfields, "flags")
}

func (self *Generator) genEnums(enums []*Enum, file string) {
	output := new(bytes.Buffer)
	w(output, "package %s\n\n", self.PackageName)
	w(output, "/*\n")
	for _, include := range self.Includes {
		w(output, "#include <%s>\n", include)
	}
	w(output, "*/\n")
	w(output, "import \"C\"\n")

	w(output, "var (\n")
	for _, f := range enums {
		w(output, "\t// %s\n", f.Name)
		for _, m := range f.Members {
			parts := strings.Split(m.CIdentifier, "_")
			goName := strings.Join(parts[1:], "_")
			w(output, "%s = C.%s(C.%s)\n", goName, f.CType, m.CIdentifier)
		}
		w(output, "\n")
	}
	w(output, ")\n")

	self.formatAndOutput(file, output.Bytes())
}
<commit_msg>generator: avoid digit prefixes<commit_after>package main

import (
	"bytes"
	"strings"
	"unicode"
)

func (self *Generator) GenEnums(ns *Namespace) {
	self.genEnums(ns.Enums, "enums")
}

func (self *Generator) GenFlags(ns *Namespace) {
	self.genEnums(ns.Bitfields, "flags")
}

func (self *Generator) genEnums(enums []*Enum, file string) {
	output := new(bytes.Buffer)
	w(output, "package %s\n\n", self.PackageName)
	w(output, "/*\n")
	for _, include := range self.Includes {
		w(output, "#include <%s>\n", include)
	}
	w(output, "*/\n")
	w(output, "import \"C\"\n")

	w(output, "var (\n")
	for _, f := range enums {
		w(output, "\t// %s\n", f.Name)
		for _, m := range f.Members {
			parts := strings.Split(m.CIdentifier, "_")
			goName := strings.Join(parts[1:], "_")
			if unicode.IsDigit(rune(goName[0])) {
				goName = strings.ToUpper(self.PackageName) + "_" + goName
			}
			w(output, "%s = C.%s(C.%s)\n", goName, f.CType, m.CIdentifier)
		}
		w(output, "\n")
	}
	w(output, ")\n")

	self.formatAndOutput(file, output.Bytes())
}
<|endoftext|>


<commit_before>package errors

import (
	"fmt"
	"strings"

	"github.com/mondough/typhon/errors/stack"
)

type Error struct {
	Code        string            `json:"code"`
	Message     string            `json:"message"`
	Params      map[string]string `json:"params"`
	StackFrames stack.Stack       `json:"stack"`
}

// Generic error codes. Each of these has their own constructor for convenience.
// You can use any string as a code, just use the `New` method.
const (
	ErrUnknown         = "err_unknown"
	ErrInternalService = "err_internal_service"
	ErrBadRequest      = "err_bad_request"
	ErrBadResponse     = "err_bad_response"
	ErrForbidden       = "err_forbidden"
	ErrUnauthorized    = "err_unauthorized"
	ErrNotFound        = "err_not_found"
	ErrTimeout         = "err_timeout"
)

// Error returns a string message of the error
// This means the Error implements the error interface
func (p *Error) Error() string {
	if p == nil {
		return ""
	}
	return p.Message
}

// StackString formats the stack as a beautiful string with newlines
func (p *Error) StackString() string {
	stackStr := ""
	for _, frame := range p.StackFrames {
		stackStr = fmt.Sprintf("%s\n  %s:%d in %s", stackStr, frame.Filename, frame.Line, frame.Method)
	}
	return stackStr
}

// VerboseString returns the error message, stack trace and contexts
func (p *Error) VerboseString() string {
	return fmt.Sprintf("%s\nParams: %+v\n%s", p.Error(), p.Params, p.StackString())
}

// New creates a new error for you. Use this if you want to pass along a custom error code.
// Otherwise use the handy shorthand factories below
func New(code string, message string, params map[string]string) *Error {
	return errorFactory(code, message, params)
}

// Wrap takes any error interface and wraps it into an Error.
// This is useful because an Error contains lots of useful goodies, like the stacktrace of the error.
// NOTE: If `err` is already an `Error` the passed contexts will be ignored
func Wrap(err error, params map[string]string) *Error {
	return WrapWithCode(err, params, ErrInternalService)
}

func WrapWithCode(err error, params map[string]string, code string) *Error {
	if err == nil {
		return nil
	}
	switch err := err.(type) {
	case *Error:
		return err
	default:
		return errorFactory(code, err.Error(), params)
	}
}

// InternalService creates a new error to represent an internal service error.
// Only use internal service error if we know very little about the error. Most
// internal service errors will come from `Wrap`ing a vanilla `error` interface
func InternalService(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrInternalService, code), message, params)
}

// BadRequest creates a new error to represent an error caused by the client sending
// an invalid request. This is non-retryable unless the request is modified.
func BadRequest(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrBadRequest, code), message, params)
}

// BadResponse creates a new error representing a failure to response with a valid response
// Examples of this would be a handler returning an invalid message format
func BadResponse(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrBadResponse, code), message, params)
}

// Timeout creates a new error representing a timeout from client to server
func Timeout(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrTimeout, code), message, params)
}

// NotFound creates a new error representing a resource that cannot be found. In some
// cases this is not an error, and would be better represented by a zero length slice of elements
func NotFound(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrNotFound, code), message, params)
}

// Forbidden creates a new error representing a resource that cannot be accessed with
// the current authorisation credentials. The user may need authorising, or if authorised,
// may not be permitted to perform this action
func Forbidden(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrForbidden, code), message, params)
}

// Unauthorized creates a new error indicating that authentication is required,
// but has either failed or not been provided.
func Unauthorized(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrUnauthorized, code), message, params)
}

// errorConstructor returns a `*Error` with the specified code, message and params.
// Builds a stack based on the current call stack
func errorFactory(code string, message string, params map[string]string) *Error {
	err := &Error{
		Code:    ErrUnknown,
		Message: message,
		Params:  map[string]string{},
	}
	if len(code) > 0 {
		err.Code = code
	}
	if params != nil {
		err.Params = params
	}

	// TODO pass in context.Context

	// Build stack and skip first three lines:
	//  - stack.go BuildStack()
	//  - errors.go errorFactory()
	//  - errors.go public constructor method
	err.StackFrames = stack.BuildStack(3)

	return err
}

func errCode(prefix, code string) string {
	if code == "" {
		return prefix
	}
	if prefix == "" {
		return code
	}
	return strings.Join([]string{prefix, code}, ".")
}
<commit_msg>Error strings should be `bad_request` rather than `err_bad_request`<commit_after>package errors

import (
	"fmt"
	"strings"

	"github.com/mondough/typhon/errors/stack"
)

type Error struct {
	Code        string            `json:"code"`
	Message     string            `json:"message"`
	Params      map[string]string `json:"params"`
	StackFrames stack.Stack       `json:"stack"`
}

// Generic error codes. Each of these has their own constructor for convenience.
// You can use any string as a code, just use the `New` method.
const (
	ErrUnknown         = "unknown"
	ErrInternalService = "internal_service"
	ErrBadRequest      = "bad_request"
	ErrBadResponse     = "bad_response"
	ErrForbidden       = "forbidden"
	ErrUnauthorized    = "unauthorized"
	ErrNotFound        = "not_found"
	ErrTimeout         = "timeout"
)

// Error returns a string message of the error
// This means the Error implements the error interface
func (p *Error) Error() string {
	if p == nil {
		return ""
	}
	return p.Message
}

// StackString formats the stack as a beautiful string with newlines
func (p *Error) StackString() string {
	stackStr := ""
	for _, frame := range p.StackFrames {
		stackStr = fmt.Sprintf("%s\n  %s:%d in %s", stackStr, frame.Filename, frame.Line, frame.Method)
	}
	return stackStr
}

// VerboseString returns the error message, stack trace and contexts
func (p *Error) VerboseString() string {
	return fmt.Sprintf("%s\nParams: %+v\n%s", p.Error(), p.Params, p.StackString())
}

// New creates a new error for you. Use this if you want to pass along a custom error code.
// Otherwise use the handy shorthand factories below
func New(code string, message string, params map[string]string) *Error {
	return errorFactory(code, message, params)
}

// Wrap takes any error interface and wraps it into an Error.
// This is useful because an Error contains lots of useful goodies, like the stacktrace of the error.
// NOTE: If `err` is already an `Error` the passed contexts will be ignored
func Wrap(err error, params map[string]string) *Error {
	return WrapWithCode(err, params, ErrInternalService)
}

func WrapWithCode(err error, params map[string]string, code string) *Error {
	if err == nil {
		return nil
	}
	switch err := err.(type) {
	case *Error:
		return err
	default:
		return errorFactory(code, err.Error(), params)
	}
}

// InternalService creates a new error to represent an internal service error.
// Only use internal service error if we know very little about the error. Most
// internal service errors will come from `Wrap`ing a vanilla `error` interface
func InternalService(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrInternalService, code), message, params)
}

// BadRequest creates a new error to represent an error caused by the client sending
// an invalid request. This is non-retryable unless the request is modified.
func BadRequest(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrBadRequest, code), message, params)
}

// BadResponse creates a new error representing a failure to response with a valid response
// Examples of this would be a handler returning an invalid message format
func BadResponse(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrBadResponse, code), message, params)
}

// Timeout creates a new error representing a timeout from client to server
func Timeout(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrTimeout, code), message, params)
}

// NotFound creates a new error representing a resource that cannot be found. In some
// cases this is not an error, and would be better represented by a zero length slice of elements
func NotFound(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrNotFound, code), message, params)
}

// Forbidden creates a new error representing a resource that cannot be accessed with
// the current authorisation credentials. The user may need authorising, or if authorised,
// may not be permitted to perform this action
func Forbidden(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrForbidden, code), message, params)
}

// Unauthorized creates a new error indicating that authentication is required,
// but has either failed or not been provided.
func Unauthorized(code, message string, params map[string]string) *Error {
	return errorFactory(errCode(ErrUnauthorized, code), message, params)
}

// errorConstructor returns a `*Error` with the specified code, message and params.
// Builds a stack based on the current call stack
func errorFactory(code string, message string, params map[string]string) *Error {
	err := &Error{
		Code:    ErrUnknown,
		Message: message,
		Params:  map[string]string{},
	}
	if len(code) > 0 {
		err.Code = code
	}
	if params != nil {
		err.Params = params
	}

	// TODO pass in context.Context

	// Build stack and skip first three lines:
	//  - stack.go BuildStack()
	//  - errors.go errorFactory()
	//  - errors.go public constructor method
	err.StackFrames = stack.BuildStack(3)

	return err
}

func errCode(prefix, code string) string {
	if code == "" {
		return prefix
	}
	if prefix == "" {
		return code
	}
	return strings.Join([]string{prefix, code}, ".")
}
<|endoftext|>


<commit_before>package errors

import (
	"encoding/json"
	"net/http"
)

type Error struct {
	Id     string `json:"id"`
	Code   int32  `json:"code"`
	Detail string `json:"detail"`
	Status string `json:"status"`
}

func (e *Error) Error() string {
	b, _ := json.Marshal(e)
	return string(b)
}

func New(id, detail string, code int32) error {
	return &Error{
		Id:     id,
		Code:   code,
		Detail: detail,
		Status: http.StatusText(int(code)),
	}
}

func Parse(err string) *Error {
	var e *Error
	errr := json.Unmarshal([]byte(err), &e)
	if errr != nil {
		e.Detail = err
	}
	return e
}

func BadRequest(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   400,
		Detail: detail,
		Status: http.StatusText(400),
	}
}

func Unauthorized(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   401,
		Detail: detail,
		Status: http.StatusText(401),
	}
}

func Forbidden(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   403,
		Detail: detail,
		Status: http.StatusText(403),
	}
}

func NotFound(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   404,
		Detail: detail,
		Status: http.StatusText(404),
	}
}

func InternalServerError(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   500,
		Detail: detail,
		Status: http.StatusText(500),
	}
}
<commit_msg>Fixes nil pointer dereference error<commit_after>package errors

import (
	"encoding/json"
	"net/http"
)

type Error struct {
	Id     string `json:"id"`
	Code   int32  `json:"code"`
	Detail string `json:"detail"`
	Status string `json:"status"`
}

func (e *Error) Error() string {
	b, _ := json.Marshal(e)
	return string(b)
}

func New(id, detail string, code int32) error {
	return &Error{
		Id:     id,
		Code:   code,
		Detail: detail,
		Status: http.StatusText(int(code)),
	}
}

func Parse(err string) *Error {
	e := new(Error)
	errr := json.Unmarshal([]byte(err), e)
	if errr != nil {
		e.Detail = err
	}
	return e
}

func BadRequest(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   400,
		Detail: detail,
		Status: http.StatusText(400),
	}
}

func Unauthorized(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   401,
		Detail: detail,
		Status: http.StatusText(401),
	}
}

func Forbidden(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   403,
		Detail: detail,
		Status: http.StatusText(403),
	}
}

func NotFound(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   404,
		Detail: detail,
		Status: http.StatusText(404),
	}
}

func InternalServerError(id, detail string) error {
	return &Error{
		Id:     id,
		Code:   500,
		Detail: detail,
		Status: http.StatusText(500),
	}
}
<|endoftext|>


<commit_before>package main

import "fmt"

func main() {
	fmt.Println("Hello!")
}<commit_msg>Solve first problem with Go<commit_after>package main

import "fmt"

func main() {
	multiples := make(map[int]bool)
	sum := 0
	for n, by3, by5 := 1, 3, 5; n < 1000; n++ {

		by3 = n * 3
		if (by3 >= 1000) {
			break;
		}

		_, ok := multiples[by3]
		if ! ok {
			sum += by3
			multiples[by3] = true
		}

		by5 = n * 5
		if (by5 >= 1000) {
			continue;
		}

		_, ok = multiples[by5]
		if ! ok {
			sum += by5
			multiples[by5] = true
		}
	}

	fmt.Printf("Sum of all the multiples of 3 or 5 below 1000 is %d.\n", sum)
}<|endoftext|>


<commit_before>package main

import (
	//"code.google.com/p/portaudio-go/portaudio"
	"fmt"
	"github.com/Hell0wor1d/go2ffmpeg/ffmpeg"
	"log"
	"os"
)

func init() {
	fmt.Printf("init app.")
}

func main() {
	if len(os.Args) <= 1 {
		fmt.Println("Usage:\n \t./ffmpeg audio_file")
		os.Exit(0)
	}
	inputfile := os.Args[1]
	//	chk := func(err error) {
	//		if err != nil {
	//			panic(err)
	//		}
	//	}
	//	framePerBuffer := 2048
	args := []string{
		"-i", inputfile,
		"-f", "mp3",
		"-",
	}
	ff := ffmpeg.NewFFmpeg(args)
	if ff == nil {
		log.Fatal("ffmpeg can not be nill.")
	}
	//defer ff.Close()
	//buffer := make([]byte, 1024)
	out, err := os.Create("/Users/kevin/filename.mp3")
	defer out.Close()
	if err != nil {
		log.Fatal(err)
	}
	ff.Read(out)
	//	portaudio.Initialize()
	//	defer portaudio.Terminate()
	//	stream, err := portaudio.OpenDefaultStream(0, 2, 44100, framePerBuffer, ff)
	//chk(err)
	//defer stream.Close()
	//chk(stream.Start())
	//if err := ff.Wait(); err != nil {
	//		log.Fatal(err)
	//	}
	//	chk(stream.Stop())
}
<commit_msg>updates<commit_after>package main

import (
	//"code.google.com/p/portaudio-go/portaudio"
	"fmt"
	"github.com/Hell0wor1d/go2ffmpeg/ffmpeg"
	"log"
	"os"
)

func init() {
	fmt.Printf("init app.")
}

func main() {
	if len(os.Args) <= 1 {
		fmt.Println("Usage:\n \t./ffmpeg audio_file")
		os.Exit(0)
	}
	inputfile := os.Args[1]

	defer func() {
		if err := recover(); err != nil {
			log.Fatal(err)
		}
	}()

	chk := func(err error) {
		if err != nil {
			panic(err)
		}
	}

	finfo, err := os.Stat(inputfile)
	chk(err)
	if !finfo.Mode().IsRegular() {
		panic("target is not a file.")
	}
	args := []string{
		"-i", inputfile,
		"-f", "mp3",
		"-",
	}
	ff := ffmpeg.NewFFmpeg(args)
	if ff == nil {
		panic("ffmpeg can not be nil.")
	}
	out, err := os.Create("/Users/kevin/filename.mp3")
	defer out.Close()
	chk(err)
	ff.Read(out)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
)

type Week int

const (
	Sun Week = iota
	Mon
	Tue
	Wed
	Thur
	Fri
	Sat
)

type Leap struct {
	days int
	leap bool
}

func (l Leap) ToString() string {
	return fmt.Sprintf("Leap:[ %d %v]", l.days, l.leap)
}

func (l Leap) String() string {
	return fmt.Sprintf("Leap: %d %v", l.days, l.leap)
}

type Month struct {
	m int
}

type Year struct {
	y int
}

type Date struct {
	Year
	Month
	Day
	Week
}

func (d Date) CountDays(s Date, w Week, d Date) {

}

func isLeap(n int) Leap {
	var l Leap
	if ((n%100 != 0) && (n%4 == 0)) || n%400 == 0 {
		l = Leap{366, true}
	} else {
		l = Leap{365, false}
	}
	return l
}

func main() {

	for i := 1889; i < 2001; i++ {
		l := isLeap(i)
		fmt.Println(l)
	}
}
<commit_msg>update<commit_after>package main

import (
	"fmt"
)

type Week int

const (
	Sun Week = iota
	Mon
	Tue
	Wed
	Thur
	Fri
	Sat
)

type Leap struct {
	days int
	leap bool
}

func (l Leap) ToString() string {
	return fmt.Sprintf("Leap:[ %d %v]", l.days, l.leap)
}

func (l Leap) String() string {
	return fmt.Sprintf("Leap: %d %v", l.days, l.leap)
}

type Day struct{
	d Week
}
type Month struct {
	m int
	num int
}

type Year struct {
	y int
	leap bool
}

type Date struct {
	Year
	Month
	Day
	Week
}

func (d *Date) CountDays(w Week, d Date) {
	d.Year.
}

func isLeap(n int) Leap {
	var l Leap
	if ((n%100 != 0) && (n%4 == 0)) || n%400 == 0 {
		l = Leap{366, true}
	} else {
		l = Leap{365, false}
	}
	return l
}

func main() {

	for i := 1889; i < 2001; i++ {
		l := isLeap(i)
		fmt.Println(l)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2021 The Vitess 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 integration

import (
	"bytes"
	"fmt"
	"sort"
	"strings"
	"testing"

	"vitess.io/vitess/go/mysql/collations"
	"vitess.io/vitess/go/mysql/collations/remote"
	"vitess.io/vitess/go/sqltypes"
)

type TextWithCollation struct {
	Text      []byte
	Collation collations.Collation
}

type RemoteCoercionResult struct {
	Expr         sqltypes.Value
	Collation    collations.Collation
	Coercibility collations.Coercibility
}

type RemoteCoercionTest interface {
	Expression() string
	Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coerce1, coerce2 collations.Coercion)
}

type testConcat struct {
	left, right *TextWithCollation
}

func (tc *testConcat) Expression() string {
	return fmt.Sprintf("CONCAT((_%s X'%x' COLLATE %q), (_%s X'%x' COLLATE %q))",
		tc.left.Collation.Charset().Name(), tc.left.Text, tc.left.Collation.Name(),
		tc.right.Collation.Charset().Name(), tc.right.Text, tc.right.Collation.Name(),
	)
}

func (tc *testConcat) Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coercion1, coercion2 collations.Coercion) {
	localCollation := defaultenv.LookupByID(local.Collation)
	if localCollation.Name() != remote.Collation.Name() {
		t.Errorf("bad collation resolved: local is %s, remote is %s", localCollation.Name(), remote.Collation.Name())
	}
	if local.Coercibility != remote.Coercibility {
		t.Errorf("bad coercibility resolved: local is %d, remote is %d", local.Coercibility, remote.Coercibility)
	}

	leftText, err := coercion1(nil, tc.left.Text)
	if err != nil {
		t.Errorf("failed to transcode left: %v", err)
		return
	}

	rightText, err := coercion2(nil, tc.right.Text)
	if err != nil {
		t.Errorf("failed to transcode right: %v", err)
		return
	}

	var concat bytes.Buffer
	concat.Write(leftText)
	concat.Write(rightText)

	if !bytes.Equal(concat.Bytes(), remote.Expr.ToBytes()) {
		t.Errorf("failed to concatenate text;\n\tCONCAT(%v COLLATE %s, %v COLLATE %s) = \n\tCONCAT(%v, %v) COLLATE %s = \n\t\t%v\n\n\texpected: %v",
			tc.left.Text, tc.left.Collation.Name(),
			tc.right.Text, tc.right.Collation.Name(),
			leftText, rightText, localCollation.Name(),
			concat.Bytes(), remote.Expr.ToBytes(),
		)
	}
}

type testComparison struct {
	left, right *TextWithCollation
}

func (tc *testComparison) Expression() string {
	return fmt.Sprintf("(_%s X'%x' COLLATE %q) = (_%s X'%x' COLLATE %q)",
		tc.left.Collation.Charset().Name(), tc.left.Text, tc.left.Collation.Name(),
		tc.right.Collation.Charset().Name(), tc.right.Text, tc.right.Collation.Name(),
	)
}

func (tc *testComparison) Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coerce1, coerce2 collations.Coercion) {
	leftText, err := coerce1(nil, tc.left.Text)
	if err != nil {
		t.Errorf("failed to transcode left: %v", err)
		return
	}

	rightText, err := coerce2(nil, tc.right.Text)
	if err != nil {
		t.Errorf("failed to transcode right: %v", err)
		return
	}

	remoteEquals := remote.Expr.ToBytes()[0] == '1'
	localCollation := defaultenv.LookupByID(local.Collation)
	localEquals := localCollation.Collate(leftText, rightText, false) == 0
	if remoteEquals != localEquals {
		t.Errorf("failed to collate %#v = %#v with collation %s (expected %v, got %v)",
			leftText, rightText, localCollation.Name(), remoteEquals, localEquals)
	}
}

func TestComparisonSemantics(t *testing.T) {
	const BaseString = "abcdABCD01234"
	var testInputs []*TextWithCollation

	conn := mysqlconn(t)
	defer conn.Close()

	for _, coll := range defaultenv.AllCollations() {
		text := verifyTranscoding(t, coll, remote.NewCollation(conn, coll.Name()), []byte(BaseString))
		testInputs = append(testInputs, &TextWithCollation{Text: text, Collation: coll})
	}
	sort.Slice(testInputs, func(i, j int) bool {
		return testInputs[i].Collation.ID() < testInputs[j].Collation.ID()
	})

	var testCases = []struct {
		name string
		make func(left, right *TextWithCollation) RemoteCoercionTest
	}{
		{
			name: "equals",
			make: func(left, right *TextWithCollation) RemoteCoercionTest {
				return &testComparison{left, right}
			},
		},
		{
			name: "concat",
			make: func(left, right *TextWithCollation) RemoteCoercionTest {
				return &testConcat{left, right}
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			for _, collA := range testInputs {
				for _, collB := range testInputs {
					left := collations.TypedCollation{
						Collation:    collA.Collation.ID(),
						Coercibility: 0,
						Repertoire:   collations.RepertoireASCII,
					}
					right := collations.TypedCollation{
						Collation:    collB.Collation.ID(),
						Coercibility: 0,
						Repertoire:   collations.RepertoireASCII,
					}
					resultLocal, coercionLocal1, coercionLocal2, errLocal := defaultenv.MergeCollations(left, right,
						collations.CoercionOptions{
							ConvertToSuperset:   true,
							ConvertWithCoercion: true,
						})

					remoteTest := tc.make(collA, collB)
					expr := remoteTest.Expression()
					query := fmt.Sprintf("SELECT CAST((%s) AS BINARY), COLLATION(%s), COERCIBILITY(%s)", expr, expr, expr)

					resultRemote, errRemote := conn.ExecuteFetch(query, 1, false)
					if errRemote != nil {
						if !strings.Contains(errRemote.Error(), "Illegal mix of collations") {
							t.Fatalf("query %s failed: %v", query, errRemote)
						}
						if errLocal == nil {
							t.Errorf("expected %s vs %s to fail coercion: %v", collA.Collation.Name(), collB.Collation.Name(), errRemote)
							continue
						}
						if !strings.HasPrefix(errRemote.Error(), errLocal.Error()) {
							t.Fatalf("bad error message: expected %q, got %q", errRemote, errLocal)
						}
						continue
					}

					if errLocal != nil {
						t.Errorf("expected %s vs %s to coerce, but they failed: %v", collA.Collation.Name(), collB.Collation.Name(), errLocal)
						continue
					}

					remoteCollation := defaultenv.LookupByName(resultRemote.Rows[0][1].ToString())
					remoteCI, _ := resultRemote.Rows[0][2].ToInt64()
					remoteTest.Test(t, &RemoteCoercionResult{
						Expr:         resultRemote.Rows[0][0],
						Collation:    remoteCollation,
						Coercibility: collations.Coercibility(remoteCI),
					}, resultLocal, coercionLocal1, coercionLocal2)
				}
			}
		})
	}
}
<commit_msg>collations: fix integration tests<commit_after>/*
Copyright 2021 The Vitess 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 integration

import (
	"bytes"
	"fmt"
	"sort"
	"strings"
	"testing"

	"vitess.io/vitess/go/mysql/collations"
	"vitess.io/vitess/go/mysql/collations/remote"
	"vitess.io/vitess/go/sqltypes"
)

type TextWithCollation struct {
	Text      []byte
	Collation collations.Collation
}

type RemoteCoercionResult struct {
	Expr         sqltypes.Value
	Collation    collations.Collation
	Coercibility collations.Coercibility
}

type RemoteCoercionTest interface {
	Expression() string
	Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coerce1, coerce2 collations.Coercion)
}

type testConcat struct {
	left, right *TextWithCollation
}

func (tc *testConcat) Expression() string {
	return fmt.Sprintf("CONCAT((_%s X'%x' COLLATE %q), (_%s X'%x' COLLATE %q))",
		tc.left.Collation.Charset().Name(), tc.left.Text, tc.left.Collation.Name(),
		tc.right.Collation.Charset().Name(), tc.right.Text, tc.right.Collation.Name(),
	)
}

func (tc *testConcat) Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coercion1, coercion2 collations.Coercion) {
	localCollation := defaultenv.LookupByID(local.Collation)
	if localCollation.Name() != remote.Collation.Name() {
		t.Errorf("bad collation resolved: local is %s, remote is %s", localCollation.Name(), remote.Collation.Name())
	}
	if local.Coercibility != remote.Coercibility {
		t.Errorf("bad coercibility resolved: local is %d, remote is %d", local.Coercibility, remote.Coercibility)
	}

	leftText, err := coercion1(nil, tc.left.Text)
	if err != nil {
		t.Errorf("failed to transcode left: %v", err)
		return
	}

	rightText, err := coercion2(nil, tc.right.Text)
	if err != nil {
		t.Errorf("failed to transcode right: %v", err)
		return
	}

	var concat bytes.Buffer
	concat.Write(leftText)
	concat.Write(rightText)

	if !bytes.Equal(concat.Bytes(), remote.Expr.ToBytes()) {
		t.Errorf("failed to concatenate text;\n\tCONCAT(%v COLLATE %s, %v COLLATE %s) = \n\tCONCAT(%v, %v) COLLATE %s = \n\t\t%v\n\n\texpected: %v",
			tc.left.Text, tc.left.Collation.Name(),
			tc.right.Text, tc.right.Collation.Name(),
			leftText, rightText, localCollation.Name(),
			concat.Bytes(), remote.Expr.ToBytes(),
		)
	}
}

type testComparison struct {
	left, right *TextWithCollation
}

func (tc *testComparison) Expression() string {
	return fmt.Sprintf("(_%s X'%x' COLLATE %q) = (_%s X'%x' COLLATE %q)",
		tc.left.Collation.Charset().Name(), tc.left.Text, tc.left.Collation.Name(),
		tc.right.Collation.Charset().Name(), tc.right.Text, tc.right.Collation.Name(),
	)
}

func (tc *testComparison) Test(t *testing.T, remote *RemoteCoercionResult, local collations.TypedCollation, coerce1, coerce2 collations.Coercion) {
	leftText, err := coerce1(nil, tc.left.Text)
	if err != nil {
		t.Errorf("failed to transcode left: %v", err)
		return
	}

	rightText, err := coerce2(nil, tc.right.Text)
	if err != nil {
		t.Errorf("failed to transcode right: %v", err)
		return
	}

	remoteEquals := remote.Expr.ToBytes()[0] == '1'
	localCollation := defaultenv.LookupByID(local.Collation)
	localEquals := localCollation.Collate(leftText, rightText, false) == 0
	if remoteEquals != localEquals {
		t.Errorf("failed to collate %#v = %#v with collation %s (expected %v, got %v)",
			leftText, rightText, localCollation.Name(), remoteEquals, localEquals)
	}
}

func TestComparisonSemantics(t *testing.T) {
	const BaseString = "abcdABCD01234"
	var testInputs []*TextWithCollation

	conn := mysqlconn(t)
	defer conn.Close()

	for _, coll := range defaultenv.AllCollations() {
		text := verifyTranscoding(t, coll, remote.NewCollation(conn, coll.Name()), []byte(BaseString))
		testInputs = append(testInputs, &TextWithCollation{Text: text, Collation: coll})
	}
	sort.Slice(testInputs, func(i, j int) bool {
		return testInputs[i].Collation.ID() < testInputs[j].Collation.ID()
	})

	var testCases = []struct {
		name string
		make func(left, right *TextWithCollation) RemoteCoercionTest
	}{
		{
			name: "equals",
			make: func(left, right *TextWithCollation) RemoteCoercionTest {
				return &testComparison{left, right}
			},
		},
		{
			name: "concat",
			make: func(left, right *TextWithCollation) RemoteCoercionTest {
				return &testConcat{left, right}
			},
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			for _, collA := range testInputs {
				for _, collB := range testInputs {
					left := collations.TypedCollation{
						Collation:    collA.Collation.ID(),
						Coercibility: 0,
						Repertoire:   collations.RepertoireASCII,
					}
					right := collations.TypedCollation{
						Collation:    collB.Collation.ID(),
						Coercibility: 0,
						Repertoire:   collations.RepertoireASCII,
					}
					resultLocal, coercionLocal1, coercionLocal2, errLocal := defaultenv.MergeCollations(left, right,
						collations.CoercionOptions{
							ConvertToSuperset:   true,
							ConvertWithCoercion: true,
						})

					// for strings that do not coerce, replace with a no-op coercion function
					if coercionLocal1 == nil {
						coercionLocal1 = func(_, in []byte) ([]byte, error) { return in, nil }
					}
					if coercionLocal2 == nil {
						coercionLocal2 = func(_, in []byte) ([]byte, error) { return in, nil }
					}

					remoteTest := tc.make(collA, collB)
					expr := remoteTest.Expression()
					query := fmt.Sprintf("SELECT CAST((%s) AS BINARY), COLLATION(%s), COERCIBILITY(%s)", expr, expr, expr)

					resultRemote, errRemote := conn.ExecuteFetch(query, 1, false)
					if errRemote != nil {
						if !strings.Contains(errRemote.Error(), "Illegal mix of collations") {
							t.Fatalf("query %s failed: %v", query, errRemote)
						}
						if errLocal == nil {
							t.Errorf("expected %s vs %s to fail coercion: %v", collA.Collation.Name(), collB.Collation.Name(), errRemote)
							continue
						}
						if !strings.HasPrefix(errRemote.Error(), errLocal.Error()) {
							t.Fatalf("bad error message: expected %q, got %q", errRemote, errLocal)
						}
						continue
					}

					if errLocal != nil {
						t.Errorf("expected %s vs %s to coerce, but they failed: %v", collA.Collation.Name(), collB.Collation.Name(), errLocal)
						continue
					}

					remoteCollation := defaultenv.LookupByName(resultRemote.Rows[0][1].ToString())
					remoteCI, _ := resultRemote.Rows[0][2].ToInt64()
					remoteTest.Test(t, &RemoteCoercionResult{
						Expr:         resultRemote.Rows[0][0],
						Collation:    remoteCollation,
						Coercibility: collations.Coercibility(remoteCI),
					}, resultLocal, coercionLocal1, coercionLocal2)
				}
			}
		})
	}
}
<|endoftext|>