<commit_before>package y

import (
	"database/sql"
	"fmt"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	// models work with mysql only
	_ "github.com/go-sql-driver/mysql"
)

const (
	FixturedID   int64 = 1
	FixturedName       = "foo"
)

var (
	dsn string
)

func init() {
	env := func(key, defaultValue string) string {
		if value := os.Getenv(key); value != "" {
			return value
		}
		return defaultValue
	}
	user := env("MYSQL_TEST_USER", "root")
	pass := env("MYSQL_TEST_PASS", "")
	prot := env("MYSQL_TEST_PROT", "tcp")
	addr := env("MYSQL_TEST_ADDR", "localhost:3306")
	dbname := env("MYSQL_TEST_DBNAME", "y_test")
	netAddr := fmt.Sprintf("%s(%s)", prot, addr)
	dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true", user, pass, netAddr, dbname)
}

var _ = Describe("Proxy", func() {
	var db *sql.DB

	type Fixtured struct {
		ID   int64 `y:",pk,autoincr"`
		Name string
	}

	BeforeEach(func() {
		db, _ = sql.Open("mysql", dsn)
		_, err := db.Exec(`
      CREATE TABLE y_test (
        id INTEGER PRIMARY KEY AUTO_INCREMENT,
        name TEXT,
        _version INTEGER
      )`)
		if err != nil {
			panic(err.Error())
		}
	})

	AfterEach(func() {
		db.Exec("DROP TABLE y_test")
		db.Close()
	})

	Context("when one item created", func() {
		type YTest struct {
			Fixtured
		}

		var (
			err   error
			ytest YTest
		)

		BeforeEach(func() {
			ytest = YTest{Fixtured{Name: FixturedName}}
			_, err = New(&ytest).Put(db)
		})

		It("no error occuried", func() {
			Expect(err).To(BeNil())
		})
		It("the primary key is not empty", func() {
			Expect(ytest.ID).To(Equal(FixturedID))
		})
	})

	Context("when one item loaded", func() {
		type YTest struct {
			Fixtured
		}

		var (
			err   error
			ytest YTest
		)

		BeforeEach(func() {
			// insert a fixture
			New(&YTest{Fixtured{FixturedID, FixturedName}}).Put(db)
			// load the testable bean
			ytest = YTest{Fixtured{ID: FixturedID}}
			err = New(&ytest).Load(db)
		})

		It("no error occuried", func() {
			Expect(err).To(BeNil())
		})
		It("the name is not empty", func() {
			Expect(ytest.Name).To(Equal(FixturedName))
		})
	})

	Context("when an item updated", func() {
		type YTest struct {
			Fixtured
			Versionable
		}

		var (
			err   error
			ytest YTest
		)

		BeforeEach(func() {
			ytest = YTest{Fixtured{FixturedID, FixturedName}, MakeVersionable(1)}
			// insert a fixture
			New(&ytest).Put(db)
			// update the item
			err = New(&ytest).Update(db, Values{"name": "bar"})
		})

		It("no error occuried", func() {
			Expect(err).To(BeNil())
		})
		It("the name is changed", func() {
			Expect(ytest.Name).To(Equal("bar"))
		})
		It("the versions is updated", func() {
			Expect(ytest.Version.Int64).To(Equal(int64(2)))
		})
	})
})
<commit_msg>added graceful mysql connection checking<commit_after>package y

import (
	"database/sql"
	"fmt"
	"net"
	"os"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	// models work with mysql only
	_ "github.com/go-sql-driver/mysql"
)

const (
	FixturedID   int64 = 1
	FixturedName       = "foo"
)

var (
	dsn       string
	available bool
)

func init() {
	env := func(key, defaultValue string) string {
		if value := os.Getenv(key); value != "" {
			return value
		}
		return defaultValue
	}
	user := env("MYSQL_TEST_USER", "root")
	pass := env("MYSQL_TEST_PASS", "")
	prot := env("MYSQL_TEST_PROT", "tcp")
	addr := env("MYSQL_TEST_ADDR", "localhost:3306")
	dbname := env("MYSQL_TEST_DBNAME", "y_test")
	netAddr := fmt.Sprintf("%s(%s)", prot, addr)
	dsn = fmt.Sprintf("%s:%s@%s/%s?timeout=30s&strict=true", user, pass, netAddr, dbname)
	c, err := net.Dial(prot, addr)
	if err == nil {
		available = true
		c.Close()
	}
}

var _ = Describe("Proxy", func() {
	var db *sql.DB

	type Fixtured struct {
		ID   int64 `y:",pk,autoincr"`
		Name string
	}

	// common setup
	BeforeEach(func() {
		if !available {
			Skip(fmt.Sprintf("MySQL server not running on %s", dsn))
		}
		db, _ = sql.Open("mysql", dsn)
		_, err := db.Exec(`
      CREATE TABLE y_test (
        id INTEGER PRIMARY KEY AUTO_INCREMENT,
        name TEXT,
        _version INTEGER
      )`)
		if err != nil {
			Skip(err.Error())
		}
	})

	// common teardown
	AfterEach(func() {
		db.Exec("DROP TABLE y_test")
		db.Close()
	})

	Context("when one item created", func() {
		type YTest struct {
			Fixtured
		}

		var (
			err   error
			ytest YTest
		)

		BeforeEach(func() {
			ytest = YTest{Fixtured{Name: FixturedName}}
			_, err = New(&ytest).Put(db)
		})

		It("no error occuried", func() {
			Expect(err).To(BeNil())
		})
		It("the primary key is not empty", func() {
			Expect(ytest.ID).To(Equal(FixturedID))
		})
	})

	Context("when one item loaded", func() {
		type YTest struct {
			Fixtured
		}

		var (
			err   error
			ytest YTest
		)

		BeforeEach(func() {
			// insert a fixture
			New(&YTest{Fixtured{FixturedID, FixturedName}}).Put(db)
			// load the testable bean
			ytest = YTest{Fixtured{ID: FixturedID}}
			err = New(&ytest).Load(db)
		})

		It("no error occuried", func() {
			Expect(err).To(BeNil())
		})
		It("the name is not empty", func() {
			Expect(ytest.Name).To(Equal(FixturedName))
		})
	})

	Context("when an item updated", func() {
		type YTest struct {
			Fixtured
			Versionable
		}

		var (
			err   error
			ytest YTest
		)

		BeforeEach(func() {
			ytest = YTest{Fixtured{FixturedID, FixturedName}, MakeVersionable(1)}
			// insert a fixture
			New(&ytest).Put(db)
			// update the item
			err = New(&ytest).Update(db, Values{"name": "bar"})
		})

		It("no error occuried", func() {
			Expect(err).To(BeNil())
		})
		It("the name is changed", func() {
			Expect(ytest.Name).To(Equal("bar"))
		})
		It("the version is updated", func() {
			Expect(ytest.Version.Int64).To(Equal(int64(2)))
		})
	})
})
<|endoftext|>


<commit_before>// Copyright 2013 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goproperties

import (
	"fmt"
	"io"
	"strconv"
	"strings"
	"unicode/utf8"
)

type Properties struct {
	// Pre-/Postfix for property expansion.
	Prefix  string
	Postfix string

	m map[string]string
}

// NewProperties creates a new Properties struct with the default
// configuration for "${key}" expressions.
func NewProperties() *Properties {
	return &Properties{
		Prefix:  "${",
		Postfix: "}",
		m:       make(map[string]string),
	}
}

// Get returns the expanded value for the given key if exists. Otherwise, ok is false.
func (p *Properties) Get(key string) (value string, ok bool) {
	v, ok := p.m[key]
	if !ok {
		return "", false
	}

	expanded, err := p.expand(v)

	// we guarantee that the expanded value is free of
	// circular references and malformed expressions
	// so we panic if we still get an error here.
	if err != nil {
		panic(fmt.Errorf("%s in %q", err, key+" = "+v))
	}

	return expanded, true
}

// GetBool checks if the expanded value is one of '1', 'yes',
// 'true' or 'on' if the key exists. The comparison is case-insensitive.
// If the key does not exist the default value is returned.
func (p *Properties) GetBool(key string, def bool) bool {
	if v, ok := p.Get(key); ok {
		v = strings.ToLower(v)
		return v == "1" || v == "true" || v == "yes" || v == "on"
	}
	return def
}

// GetFloat64 parses the expanded value as a float64 if the key exists.
// If key does not exist or the value cannot be parsed the default
// value is returned.
func (p *Properties) GetFloat64(key string, def float64) float64 {
	if v, ok := p.Get(key); ok {
		n, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return def
		}
		return n
	}
	return def
}

// GetInt64 parses the expanded value as an int if the key exists.
// If key does not exist or the value cannot be parsed the default
// value is returned.
func (p *Properties) GetInt64(key string, def int64) int64 {
	if v, ok := p.Get(key); ok {
		n, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return def
		}
		return n
	}
	return def
}

// GetUint64 parses the expanded value as an uint64 if the key exists.
// If key does not exist or the value cannot be parsed the default
// value is returned.
func (p *Properties) GetUint64(key string, def uint64) uint64 {
	if v, ok := p.Get(key); ok {
		n, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			return def
		}
		return n
	}
	return def
}

// GetString returns the expanded value for the given key if exists or the default value otherwise.
func (p *Properties) GetString(key, def string) string {
	if v, ok := p.Get(key); ok {
		return v
	}
	return def
}

// Len returns the number of keys.
func (p *Properties) Len() int {
	return len(p.m)
}

// Set sets the property key to the corresponding value.
// If a value for key existed before then ok is true and prev
// contains the previous value. If the value contains a
// circular reference or a malformed expression then
// an error is returned.
func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
	// TODO(frank): Check for invalid unicode literals since this is currently done in the lexer.
	_, err = p.expand(value)
	if err != nil {
		return "", false, err
	}

	v, ok := p.Get(key)
	p.m[key] = value
	return v, ok, nil
}

// String returns a string of all expanded 'key = value' pairs.
func (p *Properties) String() string {
	var s string
	for key, _ := range p.m {
		value, _ := p.Get(key)
		s = fmt.Sprintf("%s%s = %s\n", s, key, value)
	}
	return s
}

// Write writes all unexpanded 'key = value' pairs to the given writer.
func (p *Properties) Write(w io.Writer, enc Encoding) (int, error) {
	total := 0
	for key, value := range p.m {
		s := fmt.Sprintf("%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
		n, err := w.Write([]byte(s))
		if err != nil {
			return total, err
		}
		total += n
	}
	return total, nil
}

// ----------------------------------------------------------------------------

// check expands all values and returns an error if a circular reference or
// a malformed expression was found.
func (p *Properties) check() error {
	for _, value := range p.m {
		if _, err := p.expand(value); err != nil {
			return err
		}
	}
	return nil
}

// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
// The function keeps track of the keys that were already expanded and stops if it
// detects a circular reference or a malformed expression.
func (p *Properties) expand(input string) (string, error) {
	// no pre/postfix -> nothing to expand
	if p.Prefix == "" && p.Postfix == "" {
		return input, nil
	}

	return p.doExpand(input, make(map[string]bool))
}

func (p *Properties) doExpand(s string, keys map[string]bool) (string, error) {
	a := strings.Index(s, p.Prefix)
	if a == -1 {
		return s, nil
	}

	b := strings.Index(s[a:], p.Postfix)
	if b == -1 {
		return "", fmt.Errorf("Malformed expression")
	}

	key := s[a+len(p.Prefix) : b-len(p.Postfix)+1]

	if _, ok := keys[key]; ok {
		return "", fmt.Errorf("Circular reference")
	}

	val, ok := p.m[key]
	if !ok {
		val = ""
	}

	// remember that we've seen the key
	keys[key] = true

	return p.doExpand(s[:a]+val+s[b+1:], keys)
}

// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
func encode(s string, special string, enc Encoding) string {
	switch enc {
	case UTF8:
		return encodeUtf8(s, special)
	case ISO_8859_1:
		return encodeIso(s, special)
	default:
		panic(fmt.Sprintf("Unsupported encoding %v", enc))
	}
}

func encodeUtf8(s string, special string) string {
	v := ""
	for pos := 0; pos < len(s); {
		r, w := utf8.DecodeRuneInString(s[pos:])
		pos += w
		v += escape(r, special)
	}
	return v
}

func encodeIso(s string, special string) string {
	var r rune
	var w int
	var v string
	for pos := 0; pos < len(s); {
		switch r, w = utf8.DecodeRuneInString(s[pos:]); {
		case r < 1<<8: // single byte rune -> escape special chars only
			v += escape(r, special)
		case r < 1<<16: // two byte rune -> unicode literal
			v += fmt.Sprintf("\\u%04x", r)
		default: // more than two bytes per rune -> can't encode
			v += "?"
		}
		pos += w
	}
	return v
}

func escape(r rune, special string) string {
	switch r {
	case '\f':
		return "\\f"
	case '\n':
		return "\\n"
	case '\r':
		return "\\r"
	case '\t':
		return "\\t"
	default:
		if strings.ContainsRune(special, r) {
			return "\\" + string(r)
		}
		return string(r)
	}
}
<commit_msg>Replace TODO with proper BUG comments.<commit_after>// Copyright 2013 Frank Schroeder. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package goproperties

// BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer.
// BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used.

import (
	"fmt"
	"io"
	"strconv"
	"strings"
	"unicode/utf8"
)

type Properties struct {
	// Pre-/Postfix for property expansion.
	Prefix  string
	Postfix string

	m map[string]string
}

// NewProperties creates a new Properties struct with the default
// configuration for "${key}" expressions.
func NewProperties() *Properties {
	return &Properties{
		Prefix:  "${",
		Postfix: "}",
		m:       make(map[string]string),
	}
}

// Get returns the expanded value for the given key if exists. Otherwise, ok is false.
func (p *Properties) Get(key string) (value string, ok bool) {
	v, ok := p.m[key]
	if !ok {
		return "", false
	}

	expanded, err := p.expand(v)

	// we guarantee that the expanded value is free of
	// circular references and malformed expressions
	// so we panic if we still get an error here.
	if err != nil {
		panic(fmt.Errorf("%s in %q", err, key+" = "+v))
	}

	return expanded, true
}

// GetBool checks if the expanded value is one of '1', 'yes',
// 'true' or 'on' if the key exists. The comparison is case-insensitive.
// If the key does not exist the default value is returned.
func (p *Properties) GetBool(key string, def bool) bool {
	if v, ok := p.Get(key); ok {
		v = strings.ToLower(v)
		return v == "1" || v == "true" || v == "yes" || v == "on"
	}
	return def
}

// GetFloat64 parses the expanded value as a float64 if the key exists.
// If key does not exist or the value cannot be parsed the default
// value is returned.
func (p *Properties) GetFloat64(key string, def float64) float64 {
	if v, ok := p.Get(key); ok {
		n, err := strconv.ParseFloat(v, 64)
		if err != nil {
			return def
		}
		return n
	}
	return def
}

// GetInt64 parses the expanded value as an int if the key exists.
// If key does not exist or the value cannot be parsed the default
// value is returned.
func (p *Properties) GetInt64(key string, def int64) int64 {
	if v, ok := p.Get(key); ok {
		n, err := strconv.ParseInt(v, 10, 64)
		if err != nil {
			return def
		}
		return n
	}
	return def
}

// GetUint64 parses the expanded value as an uint64 if the key exists.
// If key does not exist or the value cannot be parsed the default
// value is returned.
func (p *Properties) GetUint64(key string, def uint64) uint64 {
	if v, ok := p.Get(key); ok {
		n, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			return def
		}
		return n
	}
	return def
}

// GetString returns the expanded value for the given key if exists or the default value otherwise.
func (p *Properties) GetString(key, def string) string {
	if v, ok := p.Get(key); ok {
		return v
	}
	return def
}

// Len returns the number of keys.
func (p *Properties) Len() int {
	return len(p.m)
}

// Set sets the property key to the corresponding value.
// If a value for key existed before then ok is true and prev
// contains the previous value. If the value contains a
// circular reference or a malformed expression then
// an error is returned.
func (p *Properties) Set(key, value string) (prev string, ok bool, err error) {
	_, err = p.expand(value)
	if err != nil {
		return "", false, err
	}

	v, ok := p.Get(key)
	p.m[key] = value
	return v, ok, nil
}

// String returns a string of all expanded 'key = value' pairs.
func (p *Properties) String() string {
	var s string
	for key, _ := range p.m {
		value, _ := p.Get(key)
		s = fmt.Sprintf("%s%s = %s\n", s, key, value)
	}
	return s
}

// Write writes all unexpanded 'key = value' pairs to the given writer.
func (p *Properties) Write(w io.Writer, enc Encoding) (int, error) {
	total := 0
	for key, value := range p.m {
		s := fmt.Sprintf("%s = %s\n", encode(key, " :", enc), encode(value, "", enc))
		n, err := w.Write([]byte(s))
		if err != nil {
			return total, err
		}
		total += n
	}
	return total, nil
}

// ----------------------------------------------------------------------------

// check expands all values and returns an error if a circular reference or
// a malformed expression was found.
func (p *Properties) check() error {
	for _, value := range p.m {
		if _, err := p.expand(value); err != nil {
			return err
		}
	}
	return nil
}

// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values.
// The function keeps track of the keys that were already expanded and stops if it
// detects a circular reference or a malformed expression.
func (p *Properties) expand(input string) (string, error) {
	// no pre/postfix -> nothing to expand
	if p.Prefix == "" && p.Postfix == "" {
		return input, nil
	}

	return p.doExpand(input, make(map[string]bool))
}

func (p *Properties) doExpand(s string, keys map[string]bool) (string, error) {
	a := strings.Index(s, p.Prefix)
	if a == -1 {
		return s, nil
	}

	b := strings.Index(s[a:], p.Postfix)
	if b == -1 {
		return "", fmt.Errorf("Malformed expression")
	}

	key := s[a+len(p.Prefix) : b-len(p.Postfix)+1]

	if _, ok := keys[key]; ok {
		return "", fmt.Errorf("Circular reference")
	}

	val, ok := p.m[key]
	if !ok {
		val = ""
	}

	// remember that we've seen the key
	keys[key] = true

	return p.doExpand(s[:a]+val+s[b+1:], keys)
}

// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters.
func encode(s string, special string, enc Encoding) string {
	switch enc {
	case UTF8:
		return encodeUtf8(s, special)
	case ISO_8859_1:
		return encodeIso(s, special)
	default:
		panic(fmt.Sprintf("Unsupported encoding %v", enc))
	}
}

func encodeUtf8(s string, special string) string {
	v := ""
	for pos := 0; pos < len(s); {
		r, w := utf8.DecodeRuneInString(s[pos:])
		pos += w
		v += escape(r, special)
	}
	return v
}

func encodeIso(s string, special string) string {
	var r rune
	var w int
	var v string
	for pos := 0; pos < len(s); {
		switch r, w = utf8.DecodeRuneInString(s[pos:]); {
		case r < 1<<8: // single byte rune -> escape special chars only
			v += escape(r, special)
		case r < 1<<16: // two byte rune -> unicode literal
			v += fmt.Sprintf("\\u%04x", r)
		default: // more than two bytes per rune -> can't encode
			v += "?"
		}
		pos += w
	}
	return v
}

func escape(r rune, special string) string {
	switch r {
	case '\f':
		return "\\f"
	case '\n':
		return "\\n"
	case '\r':
		return "\\r"
	case '\t':
		return "\\t"
	default:
		if strings.ContainsRune(special, r) {
			return "\\" + string(r)
		}
		return string(r)
	}
}
<|endoftext|>


<commit_before>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

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

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

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

package cmd

import (
	"errors"
	"os/exec"
	"time"
)

var (
	DefaultTimeout = 30 * time.Second
)

var (
	ErrNotFound                = errors.New("Executable file not found in $PATH")
	ErrTimeout                 = errors.New("Timeout")
	ErrKillProcessAfterTimeout = errors.New("Failed to kill process after timeout")
)

type Cmd struct {
	Timeout time.Duration
	name    string
	arg     []string
}

func New(name string, arg ...string) *Cmd {
	return &Cmd{
		name:    name,
		arg:     arg,
		Timeout: DefaultTimeout,
	}
}

func (c *Cmd) Run() (output string, err error) {
	cmd := exec.Command(c.name, c.arg...)
	outChan, errChan := runCmd(cmd)
	select {
	case <-time.After(c.Timeout):
		killErr := cmd.Process.Kill()
		if killErr != nil {
			// @todo:
			// If this happens that means leaving working process,
			// plus working goroutine waiting for that process to finish.
			// And since this command is going to be run over, and over again
			// we might end up with hundreds processes and goroutines hanging.
			// Maybe in such critical cases (or after n-cases) we should shutdown whole module (e.g. qan/mm/summary)
			// and notify us (developers), because this shouldn't happen in correct working program - but you never know
			return "", ErrKillProcessAfterTimeout
		}
		return "", ErrTimeout
	case err = <-errChan:
		execError, ok := err.(*exec.Error)
		if ok && execError.Err == exec.ErrNotFound {
			return "", ErrNotFound
		}
		return "", err
	case output = <-outChan:
		return output, nil
	}
}

func runCmd(cmd *exec.Cmd) (outChan chan string, errChan chan error) {
	// Below channels has buffer
	// because we might get data before we would be waiting on those channels
	outChan = make(chan string, 1)
	errChan = make(chan error, 1)
	go func() {
		output, err := cmd.Output()
		if err != nil {
			select {
			case errChan <- err:
			default:
			}
			return
		}

		select {
		case outChan <- string(output):
		default:
		}
	}()
	return outChan, errChan
}
<commit_msg>PCT-808: workaround for "HOME: parameter not set" error on jenkins + simplify cmd<commit_after>/*
   Copyright (c) 2014, Percona LLC and/or its affiliates. All rights reserved.

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

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

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

package cmd

import (
	"errors"
	"os"
	"os/exec"
	"time"
)

var (
	DefaultTimeout = 30 * time.Second
)

var (
	ErrNotFound                = errors.New("Executable file not found in $PATH")
	ErrTimeout                 = errors.New("Timeout")
	ErrKillProcessAfterTimeout = errors.New("Failed to kill process after timeout")
)

type Cmd struct {
	Timeout time.Duration
	name    string
	arg     []string
}

type result struct {
	output string
	err    error
}

func New(name string, arg ...string) *Cmd {
	return &Cmd{
		name:    name,
		arg:     arg,
		Timeout: DefaultTimeout,
	}
}

func (c *Cmd) Run() (output string, err error) {
	cmd := exec.Command(c.name, c.arg...)

	// Workaround for "HOME: parameter not set"
	if os.Getenv("HOME") == "" {
		cmd.Env = append(os.Environ(), "HOME=/root")
	}

	resultChan := runCmd(cmd)
	select {
	case <-time.After(c.Timeout):
		killErr := cmd.Process.Kill()
		if killErr != nil {
			// @todo:
			// If this happens that means leaving working process,
			// plus working goroutine waiting for that process to finish.
			// And since this command is going to be run over, and over again
			// we might end up with hundreds processes and goroutines hanging.
			// Maybe in such critical cases (or after n-cases) we should shutdown whole module (e.g. qan/mm/summary)
			// and notify us (developers), because this shouldn't happen in correct working program - but you never know
			return "", ErrKillProcessAfterTimeout
		}
		return "", ErrTimeout
	case result := <-resultChan:
		execError, ok := result.err.(*exec.Error)
		if ok && execError.Err == exec.ErrNotFound {
			return "", ErrNotFound
		}
		return result.output, result.err
	}
}

func runCmd(cmd *exec.Cmd) (resultChan chan result) {
	// Below channels has buffer
	// because we might get data before we would be waiting on this channel
	resultChan = make(chan result, 1)
	go func() {
		output, err := cmd.CombinedOutput()
		select {
		case resultChan <- result{output: string(output), err: err}:
		default:
		}
	}()
	return resultChan
}
<|endoftext|>


<commit_before>package twilio

type MessageList struct {
  Client *Client
  Start int `json:"start"`
  Total int `json:"total"`
  NumPages int `json:"num_pages"`
  Page int `json:"page"`
  PageSize int `json:"page_size"`
  End int `json:"end"`
  Uri string `json:"uri"`
  FirstPageUri string `json:"first_page_uri"`
  LastPageUri string `json:"last_page_uri"`
  NextPageUri string `json:"next_page_uri"`
  PreviousPageUri string `json"previous_page_uri"`
  Messages []Message `json:"sms_messages"`
}

func (m MessageList) list() []Message {
  return m.Messages
}
<commit_msg>Add NextPage iterator for MessageList<commit_after>package twilio

import (
  "encoding/json"
)

type MessageList struct {
  Client *Client
  Start int `json:"start"`
  Total int `json:"total"`
  NumPages int `json:"num_pages"`
  Page int `json:"page"`
  PageSize int `json:"page_size"`
  End int `json:"end"`
  Uri string `json:"uri"`
  FirstPageUri string `json:"first_page_uri"`
  LastPageUri string `json:"last_page_uri"`
  NextPageUri string `json:"next_page_uri"`
  PreviousPageUri string `json"previous_page_uri"`
  Messages []Message `json:"sms_messages"`
}

func (m MessageList) list() []Message {
  return m.Messages
}

func (currentMessageList MessageList) NextPage() (*MessageList, error) {
  var messageList *MessageList

  client := currentMessageList.Client

  body, err := client.get(nil, currentMessageList.NextPageUri)

  if err != nil {
    return messageList, err
  }

  messageList = new(MessageList)
  messageList.Client = client
  err = json.Unmarshal(body, messageList)

  return messageList, err
}
<|endoftext|>


<commit_before>package routeros_test

import (
	"io"
	"testing"

	"github.com/go-routeros/routeros"
	"github.com/go-routeros/routeros/proto"
)

func TestLogin(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ []")
		s.writeSentence(t, "!done", "=ret=abc123")
		s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
		s.writeSentence(t, "!done")
	}()

	err := c.Login("userTest", "passTest")
	if err != nil {
		t.Fatal(err)
	}
}

func TestLoginIncorrect(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ []")
		s.writeSentence(t, "!done", "=ret=abc123")
		s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
		s.writeSentence(t, "!trap", "=message=incorrect login")
		s.writeSentence(t, "!done")
	}()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "from RouterOS device: incorrect login" {
		t.Fatal(err)
	}
}

func TestLoginNoChallenge(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ []")
		s.writeSentence(t, "!done")
	}()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "RouterOS: /login: no ret (challenge) received" {
		t.Fatal(err)
	}
}

func TestLoginInvalidChallenge(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ []")
		s.writeSentence(t, "!done", "=ret=Invalid Hex String")
	}()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "RouterOS: /login: invalid ret (challenge) hex string received: encoding/hex: invalid byte: U+0049 'I'" {
		t.Fatal(err)
	}
}

func TestLoginEOF(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	s.Close()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "io: read/write on closed pipe" {
		t.Fatal(err)
	}
}

func TestCloseTwice(t *testing.T) {
	c, s := newPair(t)
	defer s.Close()
	c.Close()
	c.Close()
}

func TestAsyncTwice(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	defer s.Close()

	c.Async()

	errC := c.Async()
	err := <-errC
	want := "Async() has already been called"
	if err.Error() != want {
		t.Fatalf("Second Async()=%#q; want %#q", err, want)
	}

	err = <-errC
	if err != nil {
		t.Fatalf("Async() channel should be closed after error; got %#q", err)
	}
}

func TestRun(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!re", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done")
	}()

	sen, err := c.Run("/ip/address")
	if err != nil {
		t.Fatal(err)
	}
	want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}
}

func TestRunWithListen(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @l1 []")
		s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done", ".tag=l1")
	}()

	listen, err := c.Listen("/ip/address")
	if err != nil {
		t.Fatal(err)
	}

	sen := <-listen.Chan()
	want := "!re @l1 [{`address` `1.2.3.4/32`}]"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}

	sen = <-listen.Chan()
	if sen != nil {
		t.Fatalf("Listen() channel should be closed after EOF; got %#q", sen)
	}
	err = listen.Err()
	if err != nil {
		t.Fatal(err)
	}
}

func TestRunAsync(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	c.Async()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @r1 []")
		s.writeSentence(t, "!re", ".tag=r1", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done", ".tag=r1")
	}()

	sen, err := c.Run("/ip/address")
	if err != nil {
		t.Fatal(err)
	}
	want := "!re @r1 [{`address` `1.2.3.4/32`}]\n!done @r1 []"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}
}

func TestRunEmptySentence(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t)
		s.writeSentence(t, "!re", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done")
	}()

	sen, err := c.Run("/ip/address")
	if err != nil {
		t.Fatal(err)
	}
	want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}
}

func TestRunEOF(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err != io.EOF {
		t.Fatal(err)
	}
}

func TestRunEOFAsync(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	c.Async()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @r1 []")
		s.writeSentence(t, "!re", "=address=1.2.3.4/32")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err != io.EOF {
		t.Fatal(err)
	}
}

func TestRunInvalidSentence(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!xxx")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "unknown RouterOS reply word: !xxx" {
		t.Fatal(err)
	}
}

func TestRunTrap(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!trap", "=message=Some device error message")
		s.writeSentence(t, "!done")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "from RouterOS device: Some device error message" {
		t.Fatal(err)
	}
}

func TestRunMesagelessTrap(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!trap", "=some=unknown key")
		s.writeSentence(t, "!done")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "from RouterOS device: unknown error: !trap @ [{`some` `unknown key`}]" {
		t.Fatal(err)
	}
}

func TestRunFatal(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!fatal", "=message=Some device error message")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "from RouterOS device: Some device error message" {
		t.Fatal(err)
	}
}

func TestRunAfterClose(t *testing.T) {
	c, s := newPair(t)
	c.Close()
	s.Close()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "io: read/write on closed pipe" {
		t.Fatal(err)
	}
}

func TestListen(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address/listen @l1 []")
		s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
		s.readSentence(t, "/cancel @r2 [{`tag` `l1`}]")
		s.writeSentence(t, "!trap", "=category=2", ".tag=l1")
		s.writeSentence(t, "!done", "=tag=r2")
		s.writeSentence(t, "!done", "=tag=l1")
	}()

	c.Queue = 1
	listen, err := c.Listen("/ip/address/listen")
	if err != nil {
		t.Fatal(err)
	}
	reC := listen.Chan()

	listen.Cancel()

	sen := <-reC
	want := "!re @l1 [{`address` `1.2.3.4/32`}]"
	if sen.String() != want {
		t.Fatalf("/ip/address/listen (%s); want (%s)", sen, want)
	}

	sen = <-reC
	if sen != nil {
		t.Fatalf("Listen() channel should be closed after Close(); got %#q", sen)
	}
	err = listen.Err()
	if err != nil {
		t.Fatal(err)
	}
}

type conn struct {
	*io.PipeReader
	*io.PipeWriter
}

func (c *conn) Close() error {
	c.PipeReader.Close()
	c.PipeWriter.Close()
	return nil
}

func newPair(t *testing.T) (*routeros.Client, *fakeServer) {
	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c, err := routeros.NewClient(&conn{ar, bw})
	if err != nil {
		t.Fatal(err)
	}

	s := &fakeServer{
		proto.NewReader(br),
		proto.NewWriter(aw),
		&conn{br, aw},
	}

	return c, s
}

type fakeServer struct {
	r proto.Reader
	w proto.Writer
	io.Closer
}

func (f *fakeServer) readSentence(t *testing.T, want string) {
	sen, err := f.r.ReadSentence()
	if err != nil {
		t.Fatal(err)
	}
	if sen.String() != want {
		t.Fatalf("Sentence (%s); want (%s)", sen.String(), want)
	}
	t.Logf("< %s\n", sen)
}

func (f *fakeServer) writeSentence(t *testing.T, sentence ...string) {
	t.Logf("> %#q\n", sentence)
	f.w.BeginSentence()
	for _, word := range sentence {
		f.w.WriteWord(word)
	}
	err := f.w.EndSentence()
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>If there's no challenge, it means the login succeeded<commit_after>package routeros_test

import (
	"io"
	"testing"

	"github.com/go-routeros/routeros"
	"github.com/go-routeros/routeros/proto"
)

func TestLogin(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ [{`name` `userTest`} {`password` `passTest`}]")
		s.writeSentence(t, "!done", "=ret=abc123")
		s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
		s.writeSentence(t, "!done")
	}()

	err := c.Login("userTest", "passTest")
	if err != nil {
		t.Fatal(err)
	}
}

func TestLoginIncorrect(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ [{`name` `userTest`} {`password` `passTest`}]")
		s.writeSentence(t, "!done", "=ret=abc123")
		s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
		s.writeSentence(t, "!trap", "=message=incorrect login")
		s.writeSentence(t, "!done")
	}()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "from RouterOS device: incorrect login" {
		t.Fatal(err)
	}
}

func TestLoginNoChallenge(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ [{`name` `userTest`} {`password` `passTest`}]")
		s.writeSentence(t, "!done")
	}()

	err := c.Login("userTest", "passTest")
	if err != nil {
		t.Fatal(err)
	}
}

func TestLoginInvalidChallenge(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/login @ [{`name` `userTest`} {`password` `passTest`}]")
		s.writeSentence(t, "!done", "=ret=Invalid Hex String")
	}()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "RouterOS: /login: invalid ret (challenge) hex string received: encoding/hex: invalid byte: U+0049 'I'" {
		t.Fatal(err)
	}
}

func TestLoginEOF(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	s.Close()

	err := c.Login("userTest", "passTest")
	if err == nil {
		t.Fatalf("Login succeeded; want error")
	}
	if err.Error() != "io: read/write on closed pipe" {
		t.Fatal(err)
	}
}

func TestCloseTwice(t *testing.T) {
	c, s := newPair(t)
	defer s.Close()
	c.Close()
	c.Close()
}

func TestAsyncTwice(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	defer s.Close()

	c.Async()

	errC := c.Async()
	err := <-errC
	want := "Async() has already been called"
	if err.Error() != want {
		t.Fatalf("Second Async()=%#q; want %#q", err, want)
	}

	err = <-errC
	if err != nil {
		t.Fatalf("Async() channel should be closed after error; got %#q", err)
	}
}

func TestRun(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!re", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done")
	}()

	sen, err := c.Run("/ip/address")
	if err != nil {
		t.Fatal(err)
	}
	want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}
}

func TestRunWithListen(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @l1 []")
		s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done", ".tag=l1")
	}()

	listen, err := c.Listen("/ip/address")
	if err != nil {
		t.Fatal(err)
	}

	sen := <-listen.Chan()
	want := "!re @l1 [{`address` `1.2.3.4/32`}]"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}

	sen = <-listen.Chan()
	if sen != nil {
		t.Fatalf("Listen() channel should be closed after EOF; got %#q", sen)
	}
	err = listen.Err()
	if err != nil {
		t.Fatal(err)
	}
}

func TestRunAsync(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	c.Async()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @r1 []")
		s.writeSentence(t, "!re", ".tag=r1", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done", ".tag=r1")
	}()

	sen, err := c.Run("/ip/address")
	if err != nil {
		t.Fatal(err)
	}
	want := "!re @r1 [{`address` `1.2.3.4/32`}]\n!done @r1 []"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}
}

func TestRunEmptySentence(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t)
		s.writeSentence(t, "!re", "=address=1.2.3.4/32")
		s.writeSentence(t, "!done")
	}()

	sen, err := c.Run("/ip/address")
	if err != nil {
		t.Fatal(err)
	}
	want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
	if sen.String() != want {
		t.Fatalf("/ip/address (%s); want (%s)", sen, want)
	}
}

func TestRunEOF(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err != io.EOF {
		t.Fatal(err)
	}
}

func TestRunEOFAsync(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()
	c.Async()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @r1 []")
		s.writeSentence(t, "!re", "=address=1.2.3.4/32")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err != io.EOF {
		t.Fatal(err)
	}
}

func TestRunInvalidSentence(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!xxx")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "unknown RouterOS reply word: !xxx" {
		t.Fatal(err)
	}
}

func TestRunTrap(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!trap", "=message=Some device error message")
		s.writeSentence(t, "!done")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "from RouterOS device: Some device error message" {
		t.Fatal(err)
	}
}

func TestRunMesagelessTrap(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!trap", "=some=unknown key")
		s.writeSentence(t, "!done")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "from RouterOS device: unknown error: !trap @ [{`some` `unknown key`}]" {
		t.Fatal(err)
	}
}

func TestRunFatal(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address @ []")
		s.writeSentence(t, "!fatal", "=message=Some device error message")
	}()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "from RouterOS device: Some device error message" {
		t.Fatal(err)
	}
}

func TestRunAfterClose(t *testing.T) {
	c, s := newPair(t)
	c.Close()
	s.Close()

	_, err := c.Run("/ip/address")
	if err == nil {
		t.Fatalf("Run succeeded; want error")
	}
	if err.Error() != "io: read/write on closed pipe" {
		t.Fatal(err)
	}
}

func TestListen(t *testing.T) {
	c, s := newPair(t)
	defer c.Close()

	go func() {
		defer s.Close()
		s.readSentence(t, "/ip/address/listen @l1 []")
		s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
		s.readSentence(t, "/cancel @r2 [{`tag` `l1`}]")
		s.writeSentence(t, "!trap", "=category=2", ".tag=l1")
		s.writeSentence(t, "!done", "=tag=r2")
		s.writeSentence(t, "!done", "=tag=l1")
	}()

	c.Queue = 1
	listen, err := c.Listen("/ip/address/listen")
	if err != nil {
		t.Fatal(err)
	}
	reC := listen.Chan()

	listen.Cancel()

	sen := <-reC
	want := "!re @l1 [{`address` `1.2.3.4/32`}]"
	if sen.String() != want {
		t.Fatalf("/ip/address/listen (%s); want (%s)", sen, want)
	}

	sen = <-reC
	if sen != nil {
		t.Fatalf("Listen() channel should be closed after Close(); got %#q", sen)
	}
	err = listen.Err()
	if err != nil {
		t.Fatal(err)
	}
}

type conn struct {
	*io.PipeReader
	*io.PipeWriter
}

func (c *conn) Close() error {
	c.PipeReader.Close()
	c.PipeWriter.Close()
	return nil
}

func newPair(t *testing.T) (*routeros.Client, *fakeServer) {
	ar, aw := io.Pipe()
	br, bw := io.Pipe()

	c, err := routeros.NewClient(&conn{ar, bw})
	if err != nil {
		t.Fatal(err)
	}

	s := &fakeServer{
		proto.NewReader(br),
		proto.NewWriter(aw),
		&conn{br, aw},
	}

	return c, s
}

type fakeServer struct {
	r proto.Reader
	w proto.Writer
	io.Closer
}

func (f *fakeServer) readSentence(t *testing.T, want string) {
	sen, err := f.r.ReadSentence()
	if err != nil {
		t.Fatal(err)
	}
	if sen.String() != want {
		t.Fatalf("Sentence (%s); want (%s)", sen.String(), want)
	}
	t.Logf("< %s\n", sen)
}

func (f *fakeServer) writeSentence(t *testing.T, sentence ...string) {
	t.Logf("> %#q\n", sentence)
	f.w.BeginSentence()
	for _, word := range sentence {
		f.w.WriteWord(word)
	}
	err := f.w.EndSentence()
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package gitmedia

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

var MediaWarning = []byte("# This is a placeholder for large media, please install GitHub git-media to retrieve content\n# It is also possible you did not have the media locally, run 'git media sync' to retrieve it\n")

type Encoder struct {
	writer      io.Writer
	jsonencoder *json.Encoder
}

func NewEncoder(writer io.Writer) *Encoder {
	return &Encoder{writer, json.NewEncoder(writer)}
}

func (e *Encoder) Encode(obj interface{}) error {
	header := fmt.Sprintf("# %d\n", len(MediaWarning))
	e.writer.Write([]byte(header))
	e.writer.Write(MediaWarning)
	return e.jsonencoder.Encode(obj)
}

type Decoder struct {
	reader      io.Reader
	jsondecoder *json.Decoder
}

func NewDecoder(reader io.Reader) *Decoder {
	return &Decoder{reader, json.NewDecoder(reader)}
}

func (d *Decoder) Decode(obj interface{}) error {
	buf := make([]byte, 10)
	d.reader.Read(buf)
	slices := bytes.SplitN(buf, []byte("\n"), 2)
	headerlen, err := strconv.Atoi(string(slices[0])[2:])
	if err != nil {
		fmt.Printf("Error reading header:\n%s\n", string(buf))
		panic(err)
	}

	buf = make([]byte, headerlen-len(slices[1]))
	d.reader.Read(buf)

	return d.jsondecoder.Decode(obj)
}
<commit_msg>ンンンン ン<commit_after>package gitmedia

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

var MediaWarning = []byte("# This is a placeholder for large media, please install GitHub git-media to retrieve content\n# It is also possible you did not have the media locally, run 'git media sync' to retrieve it\n")

type Encoder struct {
	writer      io.Writer
	jsonencoder *json.Encoder
}

func NewEncoder(writer io.Writer) *Encoder {
	return &Encoder{writer, json.NewEncoder(writer)}
}

func (e *Encoder) Encode(obj interface{}) error {
	e.writer.Write(MediaWarning)
	return e.jsonencoder.Encode(obj)
}

type Decoder struct {
	reader io.Reader
}

func NewDecoder(reader io.Reader) *Decoder {
	return &Decoder{reader}
}

func (d *Decoder) Decode(obj interface{}) error {
	buf := make([]byte, 1024)
	io.ReadFull(d.reader, buf)
	slices := bytes.Split(buf, []byte("\n"))
	dec := json.NewDecoder(bytes.NewBuffer(slices[len(slices)-2]))
	return dec.Decode(obj)
}
<|endoftext|>


<commit_before>// Copyright (C) 2017 Google Inc.
//
// 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 host_test

import (
	"sync"
	"testing"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/data/id"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/core/os/device/host"
)

func TestHost(t *testing.T) {
	ctx := log.Testing(t)
	h := host.Instance(ctx)
	assert.For(ctx, "Host.ID").
		That(h.Id.ID()).NotEquals(id.ID{})
	assert.For(ctx, "Host.Name").
		That(h.Name).NotEquals("")
	assert.For(ctx, "Host.Configuration.OS.Kind").
		That(h.Configuration.OS.Kind).NotEquals(device.UnknownOS)
	assert.For(ctx, "Host.Configuration.Hardware.CPU").
		ThatString(h.Configuration.Hardware.CPU.Name).NotEquals("")
	assert.For(ctx, "Host.Configuration.Hardware.GPU").
		ThatString(h.Configuration.Hardware.GPU.Name).NotEquals("")
}

func TestHostConcurrent(t *testing.T) {
	ctx := log.Testing(t)
	hosts := make([]*device.Instance, 1000)
	wg := sync.WaitGroup{}
	for i := range hosts {
		i := i
		wg.Add(1)
		go func() {
			hosts[i] = host.Instance(ctx)
			wg.Done()
		}()
	}
	wg.Wait()
	for i, h := range hosts[1:] {
		assert.For(ctx, "i=%v", i).That(h).DeepEquals(hosts[0])
	}
}
<commit_msg>core/os/device: Don’t test for GPU name. Nothing sets it yet.<commit_after>// Copyright (C) 2017 Google Inc.
//
// 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 host_test

import (
	"sync"
	"testing"

	"github.com/google/gapid/core/assert"
	"github.com/google/gapid/core/data/id"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/device"
	"github.com/google/gapid/core/os/device/host"
)

func TestHost(t *testing.T) {
	ctx := log.Testing(t)
	h := host.Instance(ctx)
	assert.For(ctx, "Host.ID").
		That(h.Id.ID()).NotEquals(id.ID{})
	assert.For(ctx, "Host.Name").
		That(h.Name).NotEquals("")
	assert.For(ctx, "Host.Configuration.OS.Kind").
		That(h.Configuration.OS.Kind).NotEquals(device.UnknownOS)
	assert.For(ctx, "Host.Configuration.Hardware.CPU").
		ThatString(h.Configuration.Hardware.CPU.Name).NotEquals("")
}

func TestHostConcurrent(t *testing.T) {
	ctx := log.Testing(t)
	hosts := make([]*device.Instance, 1000)
	wg := sync.WaitGroup{}
	for i := range hosts {
		i := i
		wg.Add(1)
		go func() {
			hosts[i] = host.Instance(ctx)
			wg.Done()
		}()
	}
	wg.Wait()
	for i, h := range hosts[1:] {
		assert.For(ctx, "i=%v", i).That(h).DeepEquals(hosts[0])
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package ingress

import (
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

// BackendByNameServers sorts upstreams by name
type BackendByNameServers []*Backend

func (c BackendByNameServers) Len() int      { return len(c) }
func (c BackendByNameServers) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c BackendByNameServers) Less(i, j int) bool {

	return c[i].Name < c[j].Name
}

// EndpointByAddrPort sorts endpoints by address and port
type EndpointByAddrPort []Endpoint

func (c EndpointByAddrPort) Len() int      { return len(c) }
func (c EndpointByAddrPort) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c EndpointByAddrPort) Less(i, j int) bool {
	iName := c[i].Address
	jName := c[j].Address
	if iName != jName {
		return iName < jName
	}

	iU := c[i].Port
	jU := c[j].Port
	return iU < jU
}

// ServerByName sorts servers by name
type ServerByName []*Server

func (c ServerByName) Len() int      { return len(c) }
func (c ServerByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ServerByName) Less(i, j int) bool {
	// special case for catch all server
	if c[j].Hostname == "_" {
		return false
	}
	return c[i].Hostname < c[j].Hostname
}

// LocationByPath sorts location by path in descending order
// Location / is the last one
type LocationByPath []*Location

func (c LocationByPath) Len() int      { return len(c) }
func (c LocationByPath) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c LocationByPath) Less(i, j int) bool {
	return c[i].Path > c[j].Path
}

// SSLCert describes a SSL certificate to be used in a server
type SSLCert struct {
	api.ObjectMeta `json:"metadata,omitempty"`
	// CAFileName contains the path to the file with the root certificate
	CAFileName string `json:"caFileName"`
	// PemFileName contains the path to the file with the certificate and key concatenated
	PemFileName string `json:"pemFileName"`
	// PemSHA contains the sha1 of the pem file.
	// This is used to detect changes in the secret that contains the certificates
	PemSHA string `json:"pemSha"`
	// CN contains all the common names defined in the SSL certificate
	CN []string `json:"cn"`
}

// GetObjectKind implements the ObjectKind interface as a noop
func (s SSLCert) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
<commit_msg>Remove special check in sort server by name<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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

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

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

package ingress

import (
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
)

// BackendByNameServers sorts upstreams by name
type BackendByNameServers []*Backend

func (c BackendByNameServers) Len() int      { return len(c) }
func (c BackendByNameServers) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c BackendByNameServers) Less(i, j int) bool {

	return c[i].Name < c[j].Name
}

// EndpointByAddrPort sorts endpoints by address and port
type EndpointByAddrPort []Endpoint

func (c EndpointByAddrPort) Len() int      { return len(c) }
func (c EndpointByAddrPort) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c EndpointByAddrPort) Less(i, j int) bool {
	iName := c[i].Address
	jName := c[j].Address
	if iName != jName {
		return iName < jName
	}

	iU := c[i].Port
	jU := c[j].Port
	return iU < jU
}

// ServerByName sorts servers by name
type ServerByName []*Server

func (c ServerByName) Len() int      { return len(c) }
func (c ServerByName) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c ServerByName) Less(i, j int) bool {
	return c[i].Hostname < c[j].Hostname
}

// LocationByPath sorts location by path in descending order
// Location / is the last one
type LocationByPath []*Location

func (c LocationByPath) Len() int      { return len(c) }
func (c LocationByPath) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c LocationByPath) Less(i, j int) bool {
	return c[i].Path > c[j].Path
}

// SSLCert describes a SSL certificate to be used in a server
type SSLCert struct {
	api.ObjectMeta `json:"metadata,omitempty"`
	// CAFileName contains the path to the file with the root certificate
	CAFileName string `json:"caFileName"`
	// PemFileName contains the path to the file with the certificate and key concatenated
	PemFileName string `json:"pemFileName"`
	// PemSHA contains the sha1 of the pem file.
	// This is used to detect changes in the secret that contains the certificates
	PemSHA string `json:"pemSha"`
	// CN contains all the common names defined in the SSL certificate
	CN []string `json:"cn"`
}

// GetObjectKind implements the ObjectKind interface as a noop
func (s SSLCert) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 zulily, Inc.
 *
 * 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 sampler

import "math"

const (
	// MaxExampleKeys sets an upper bound on the number of example keys that will
	// be captured during sampling
	MaxExampleKeys = 10
	// MaxExampleElements sets an upper bound on the number of example elements that
	// will be captured during sampling
	MaxExampleElements = 10
	// MaxExampleValues sets an upper bound on the number of example values that
	// will be captured during sampling
	MaxExampleValues = 10
)

// Statistics are basic descriptive statistics that summarize data in a frequency table
type Statistics struct {
	Mean   float64
	Min    int
	Max    int
	StdDev float64
}

// powerOfTwo returns the smallest power of two that is greater than or equal to `n`
func powerOfTwo(n int) int {
	p := 1
	for p < n {
		p = p * 2
	}
	return p
}

// ComputePowerOfTwoFreq converts a frequency map into a new frequency map,
// where each map key is the smallest power of two that is greater than or
// equal to the original map key.
func ComputePowerOfTwoFreq(m map[int]int64) map[int]int64 {
	pf := make(map[int]int64)

	for k, v := range m {
		p := powerOfTwo(k)
		if existing, ok := pf[p]; ok {
			pf[p] = existing + v
		} else {
			pf[p] = v
		}
	}

	return pf
}

// ComputeStatistics computes basic descriptive statistics about a frequency map
func ComputeStatistics(m map[int]int64) Statistics {
	stats := Statistics{}
	if len(m) == 0 {
		return stats
	}

	min := math.MaxInt32
	max := math.MinInt32
	accum, count, sd := int64(0), int64(0), float64(0)

	for k, v := range m {
		if k < min {
			min = k
		}
		if k > max {
			max = k
		}
		accum += int64(k) * v
		count += v
	}

	mean := float64(accum) / float64(count)

	for k, v := range m {
		kf, vf := float64(k), float64(v)
		sd += ((kf - mean) * (kf - mean)) * vf
	}

	return Statistics{
		Mean:   mean,
		Min:    min,
		Max:    max,
		StdDev: math.Sqrt(sd / float64(count)),
	}
}

// add adds `elem` to the "set" (a map[<type>]bool is an idiomatic golang "set") if the
// current size of the set is less than `maxsize`
func add(set map[string]bool, elem string, maxsize int) {
	if len(set) >= maxsize {
		return
	}
	set[elem] = true
}

// Results stores data about sampled redis data structures. Map keys represent
// lengths/sizes, while map values represent the frequency with which those
// lengths/sizes occurred in the sampled data. Example keys are stored in
// golang "sets", which are maps with bool values.
type Results struct {
	KeyCount int64

	// Strings
	StringSizes  map[int]int64
	StringKeys   map[string]bool
	StringValues map[string]bool

	// Sets
	SetSizes        map[int]int64
	SetElementSizes map[int]int64
	SetKeys         map[string]bool
	SetElements     map[string]bool

	// Sorted Sets
	SortedSetSizes        map[int]int64
	SortedSetElementSizes map[int]int64
	SortedSetKeys         map[string]bool
	SortedSetElements     map[string]bool

	// Hashes
	HashSizes        map[int]int64
	HashElementSizes map[int]int64
	HashValueSizes   map[int]int64
	HashKeys         map[string]bool
	HashElements     map[string]bool
	HashValues       map[string]bool

	// Lists
	ListSizes        map[int]int64
	ListElementSizes map[int]int64
	ListKeys         map[string]bool
	ListElements     map[string]bool
}

// NewResults constructs a new, zero-valued Results struct
func NewResults() *Results {
	return &Results{
		StringSizes:  make(map[int]int64),
		StringKeys:   make(map[string]bool),
		StringValues: make(map[string]bool),

		SetSizes:        make(map[int]int64),
		SetElementSizes: make(map[int]int64),
		SetKeys:         make(map[string]bool),
		SetElements:     make(map[string]bool),

		SortedSetSizes:        make(map[int]int64),
		SortedSetElementSizes: make(map[int]int64),
		SortedSetKeys:         make(map[string]bool),
		SortedSetElements:     make(map[string]bool),

		HashSizes:        make(map[int]int64),
		HashElementSizes: make(map[int]int64),
		HashValueSizes:   make(map[int]int64),
		HashKeys:         make(map[string]bool),
		HashElements:     make(map[string]bool),
		HashValues:       make(map[string]bool),

		ListSizes:        make(map[int]int64),
		ListElementSizes: make(map[int]int64),
		ListKeys:         make(map[string]bool),
		ListElements:     make(map[string]bool),
	}
}

// merge inserts all key/value pairs in `b` into `a`.  If `b` contains keys
// that are present in `a`, their values will be summed
func merge(a map[int]int64, b map[int]int64) {
	for k, v := range b {
		a[k] += v
	}
}

// union performs a set union of `a` and `b`, storing the results in `a`
func union(a map[string]bool, b map[string]bool) {
	for k := range b {
		a[k] = true
	}
}

// trim creates a new set, consisting of up to `n` random members from set `s`.
// If `len(s)` < `n`, the returned map will be of length `len(s)`. Set `s`
// remains unmodified.
func trim(s map[string]bool, n int) map[string]bool {
	t := make(map[string]bool)
	// map iteration is random in golang!
	for k := range s {
		t[k] = true
		if len(t) == n {
			break
		}
	}
	return t
}

// trimAndSum removes entries from the frequency map that comprise less than
// `threshold` % of the total, returning the sum of the **original** map
func trimAndSum(m map[int]int64, threshold float64) int64 {
	var s int64
	var sum float64
	for _, v := range m {
		s += v
	}
	sum = float64(s)
	for k, v := range m {
		if float64(v)/sum <= threshold {
			delete(m, k)
		}
	}
	return s
}

// Merge adds the results from `other` into the method receiver.  This method
// can be used to combine sampling results from multiple redis instances into a
// single result set.
func (r *Results) Merge(other *Results) {
	r.KeyCount += other.KeyCount

	// union all sets
	union(r.StringKeys, other.StringKeys)
	union(r.StringValues, other.StringValues)
	union(r.SetKeys, other.SetKeys)
	union(r.SetElements, other.SetElements)
	union(r.SortedSetKeys, other.SortedSetKeys)
	union(r.SortedSetElements, other.SortedSetElements)
	union(r.HashKeys, other.HashKeys)
	union(r.HashElements, other.HashElements)
	union(r.HashValues, other.HashValues)
	union(r.ListKeys, other.ListKeys)
	union(r.ListElements, other.ListElements)

	// merge all frequency tables
	merge(r.StringSizes, other.StringSizes)
	merge(r.SetSizes, other.SetSizes)
	merge(r.SetElementSizes, other.SetElementSizes)
	merge(r.SortedSetSizes, other.SortedSetSizes)
	merge(r.SortedSetElementSizes, other.SortedSetElementSizes)
	merge(r.HashSizes, other.HashSizes)
	merge(r.HashElementSizes, other.HashElementSizes)
	merge(r.HashValueSizes, other.HashValueSizes)
	merge(r.ListSizes, other.ListSizes)
	merge(r.ListElementSizes, other.ListElementSizes)
}

func (r *Results) observeSet(key string, length int, member string) {
	r.KeyCount++
	r.SetSizes[length] += 1
	r.SetElementSizes[len(member)] += 1
	add(r.SetKeys, key, MaxExampleKeys)
	add(r.SetElements, member, MaxExampleElements)
}

func (r *Results) observeSortedSet(key string, length int, member string) {
	r.KeyCount++
	r.SortedSetSizes[length] += 1
	r.SortedSetElementSizes[len(member)] += 1
	add(r.SortedSetKeys, key, MaxExampleKeys)
	add(r.SortedSetElements, member, MaxExampleElements)
}

func (r *Results) observeHash(key string, length int, field string, value string) {
	r.KeyCount++
	r.HashSizes[length] += 1
	r.HashValueSizes[len(value)] += 1
	r.HashElementSizes[len(field)] += 1
	add(r.HashKeys, key, MaxExampleKeys)
	add(r.HashElements, field, MaxExampleElements)
	add(r.HashValues, value, MaxExampleValues)
}

func (r *Results) observeList(key string, length int, member string) {
	r.KeyCount++
	r.ListSizes[length] += 1
	r.ListElementSizes[len(member)] += 1
	add(r.ListKeys, key, MaxExampleKeys)
	add(r.ListElements, member, MaxExampleElements)
}

func (r *Results) observeString(key, value string) {
	r.KeyCount++
	r.StringSizes[len(value)] += 1
	add(r.StringKeys, key, MaxExampleKeys)
	add(r.StringValues, value, MaxExampleValues)
}
<commit_msg>Use the sample std dev instead of population std dev<commit_after>/*
 * Copyright (C) 2015 zulily, Inc.
 *
 * 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 sampler

import "math"

const (
	// MaxExampleKeys sets an upper bound on the number of example keys that will
	// be captured during sampling
	MaxExampleKeys = 10
	// MaxExampleElements sets an upper bound on the number of example elements that
	// will be captured during sampling
	MaxExampleElements = 10
	// MaxExampleValues sets an upper bound on the number of example values that
	// will be captured during sampling
	MaxExampleValues = 10
)

// Statistics are basic descriptive statistics that summarize data in a frequency table
type Statistics struct {
	Mean   float64
	Min    int
	Max    int
	StdDev float64
}

// powerOfTwo returns the smallest power of two that is greater than or equal to `n`
func powerOfTwo(n int) int {
	p := 1
	for p < n {
		p = p * 2
	}
	return p
}

// ComputePowerOfTwoFreq converts a frequency map into a new frequency map,
// where each map key is the smallest power of two that is greater than or
// equal to the original map key.
func ComputePowerOfTwoFreq(m map[int]int64) map[int]int64 {
	pf := make(map[int]int64)

	for k, v := range m {
		p := powerOfTwo(k)
		if existing, ok := pf[p]; ok {
			pf[p] = existing + v
		} else {
			pf[p] = v
		}
	}

	return pf
}

// ComputeStatistics computes basic descriptive statistics about a frequency map
func ComputeStatistics(m map[int]int64) Statistics {
	stats := Statistics{}
	if len(m) == 0 {
		return stats
	}

	min := math.MaxInt32
	max := math.MinInt32
	accum, count, sd := int64(0), int64(0), float64(0)

	for k, v := range m {
		if k < min {
			min = k
		}
		if k > max {
			max = k
		}
		accum += int64(k) * v
		count += v
	}

	mean := float64(accum) / float64(count)

	for k, v := range m {
		kf, vf := float64(k), float64(v)
		sd += ((kf - mean) * (kf - mean)) * vf
	}

	return Statistics{
		Mean:   mean,
		Min:    min,
		Max:    max,
		StdDev: math.Sqrt(sd / float64(count-1)),
	}
}

// add adds `elem` to the "set" (a map[<type>]bool is an idiomatic golang "set") if the
// current size of the set is less than `maxsize`
func add(set map[string]bool, elem string, maxsize int) {
	if len(set) >= maxsize {
		return
	}
	set[elem] = true
}

// Results stores data about sampled redis data structures. Map keys represent
// lengths/sizes, while map values represent the frequency with which those
// lengths/sizes occurred in the sampled data. Example keys are stored in
// golang "sets", which are maps with bool values.
type Results struct {
	KeyCount int64

	// Strings
	StringSizes  map[int]int64
	StringKeys   map[string]bool
	StringValues map[string]bool

	// Sets
	SetSizes        map[int]int64
	SetElementSizes map[int]int64
	SetKeys         map[string]bool
	SetElements     map[string]bool

	// Sorted Sets
	SortedSetSizes        map[int]int64
	SortedSetElementSizes map[int]int64
	SortedSetKeys         map[string]bool
	SortedSetElements     map[string]bool

	// Hashes
	HashSizes        map[int]int64
	HashElementSizes map[int]int64
	HashValueSizes   map[int]int64
	HashKeys         map[string]bool
	HashElements     map[string]bool
	HashValues       map[string]bool

	// Lists
	ListSizes        map[int]int64
	ListElementSizes map[int]int64
	ListKeys         map[string]bool
	ListElements     map[string]bool
}

// NewResults constructs a new, zero-valued Results struct
func NewResults() *Results {
	return &Results{
		StringSizes:  make(map[int]int64),
		StringKeys:   make(map[string]bool),
		StringValues: make(map[string]bool),

		SetSizes:        make(map[int]int64),
		SetElementSizes: make(map[int]int64),
		SetKeys:         make(map[string]bool),
		SetElements:     make(map[string]bool),

		SortedSetSizes:        make(map[int]int64),
		SortedSetElementSizes: make(map[int]int64),
		SortedSetKeys:         make(map[string]bool),
		SortedSetElements:     make(map[string]bool),

		HashSizes:        make(map[int]int64),
		HashElementSizes: make(map[int]int64),
		HashValueSizes:   make(map[int]int64),
		HashKeys:         make(map[string]bool),
		HashElements:     make(map[string]bool),
		HashValues:       make(map[string]bool),

		ListSizes:        make(map[int]int64),
		ListElementSizes: make(map[int]int64),
		ListKeys:         make(map[string]bool),
		ListElements:     make(map[string]bool),
	}
}

// merge inserts all key/value pairs in `b` into `a`.  If `b` contains keys
// that are present in `a`, their values will be summed
func merge(a map[int]int64, b map[int]int64) {
	for k, v := range b {
		a[k] += v
	}
}

// union performs a set union of `a` and `b`, storing the results in `a`
func union(a map[string]bool, b map[string]bool) {
	for k := range b {
		a[k] = true
	}
}

// trim creates a new set, consisting of up to `n` random members from set `s`.
// If `len(s)` < `n`, the returned map will be of length `len(s)`. Set `s`
// remains unmodified.
func trim(s map[string]bool, n int) map[string]bool {
	t := make(map[string]bool)
	// map iteration is random in golang!
	for k := range s {
		t[k] = true
		if len(t) == n {
			break
		}
	}
	return t
}

// trimAndSum removes entries from the frequency map that comprise less than
// `threshold` % of the total, returning the sum of the **original** map
func trimAndSum(m map[int]int64, threshold float64) int64 {
	var s int64
	var sum float64
	for _, v := range m {
		s += v
	}
	sum = float64(s)
	for k, v := range m {
		if float64(v)/sum <= threshold {
			delete(m, k)
		}
	}
	return s
}

// Merge adds the results from `other` into the method receiver.  This method
// can be used to combine sampling results from multiple redis instances into a
// single result set.
func (r *Results) Merge(other *Results) {
	r.KeyCount += other.KeyCount

	// union all sets
	union(r.StringKeys, other.StringKeys)
	union(r.StringValues, other.StringValues)
	union(r.SetKeys, other.SetKeys)
	union(r.SetElements, other.SetElements)
	union(r.SortedSetKeys, other.SortedSetKeys)
	union(r.SortedSetElements, other.SortedSetElements)
	union(r.HashKeys, other.HashKeys)
	union(r.HashElements, other.HashElements)
	union(r.HashValues, other.HashValues)
	union(r.ListKeys, other.ListKeys)
	union(r.ListElements, other.ListElements)

	// merge all frequency tables
	merge(r.StringSizes, other.StringSizes)
	merge(r.SetSizes, other.SetSizes)
	merge(r.SetElementSizes, other.SetElementSizes)
	merge(r.SortedSetSizes, other.SortedSetSizes)
	merge(r.SortedSetElementSizes, other.SortedSetElementSizes)
	merge(r.HashSizes, other.HashSizes)
	merge(r.HashElementSizes, other.HashElementSizes)
	merge(r.HashValueSizes, other.HashValueSizes)
	merge(r.ListSizes, other.ListSizes)
	merge(r.ListElementSizes, other.ListElementSizes)
}

func (r *Results) observeSet(key string, length int, member string) {
	r.KeyCount++
	r.SetSizes[length] += 1
	r.SetElementSizes[len(member)] += 1
	add(r.SetKeys, key, MaxExampleKeys)
	add(r.SetElements, member, MaxExampleElements)
}

func (r *Results) observeSortedSet(key string, length int, member string) {
	r.KeyCount++
	r.SortedSetSizes[length] += 1
	r.SortedSetElementSizes[len(member)] += 1
	add(r.SortedSetKeys, key, MaxExampleKeys)
	add(r.SortedSetElements, member, MaxExampleElements)
}

func (r *Results) observeHash(key string, length int, field string, value string) {
	r.KeyCount++
	r.HashSizes[length] += 1
	r.HashValueSizes[len(value)] += 1
	r.HashElementSizes[len(field)] += 1
	add(r.HashKeys, key, MaxExampleKeys)
	add(r.HashElements, field, MaxExampleElements)
	add(r.HashValues, value, MaxExampleValues)
}

func (r *Results) observeList(key string, length int, member string) {
	r.KeyCount++
	r.ListSizes[length] += 1
	r.ListElementSizes[len(member)] += 1
	add(r.ListKeys, key, MaxExampleKeys)
	add(r.ListElements, member, MaxExampleElements)
}

func (r *Results) observeString(key, value string) {
	r.KeyCount++
	r.StringSizes[len(value)] += 1
	add(r.StringKeys, key, MaxExampleKeys)
	add(r.StringValues, value, MaxExampleValues)
}
<|endoftext|>


<commit_before>
// LoL Cruncher - A Historical League of Legends Statistics Tracker
// Copyright (C) 2015  Jason Chu (1lann) 1lanncontact@gmail.com

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

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

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

package riotapi

import (
	"cruncher/app/models/dataFormat"
	"github.com/revel/revel"
	"net/http"
	"io/ioutil"
	"time"
	"encoding/json"
	"errors"
	"strconv"
)

var apiKey string

type Player struct {
	ChampionId int
	TeamId int
}

type Stats struct {
	GoldEarned int
	WardPlaced int
	WardKilled int
	Win bool
	DoubleKills int
	TripleKills int
	QuadraKills int
	PentaKills int
	UnrealKills int
	NeutralMinionsKilled int
	TimePlayed int
	ChampionsKilled int
	Assists int
	NumDeaths int
	MinionsKilled int
}

type Game struct {
	FellowPlayers []Player
	GameType string
	GameId int
	TeamId int
	GameMode string
	ChampionId int
	CreateDate int64
	SubType string
	Stats Stats
}

type gameHistory struct {
	Games []Game
}

func convertGame(game Game) dataFormat.Game {
	isMatchedGame := (game.GameType == "MATCHED_GAME")
	isClassicGame := (game.GameMode == "CLASSIC" || game.GameMode == "ARAM")

	createTime := time.Unix(int64(game.CreateDate / 1000), 0)
	createYear := strconv.Itoa(createTime.Year())
	createMonth := strconv.Itoa(int(createTime.Month()))

	return dataFormat.Game {
		DidWin: game.Stats.Win,
		IsOnBlue: (game.TeamId == 100),
		IsNormals: (isMatchedGame && isClassicGame),
		ChampionId: strconv.Itoa(game.ChampionId),
		Duration: uint32(game.Stats.TimePlayed),
		Id: strconv.Itoa(game.GameId),
		Type: game.SubType,
		Kills: uint32(game.Stats.ChampionsKilled),
		Assists: uint32(game.Stats.Assists),
		Deaths: uint32(game.Stats.NumDeaths),
		DoubleKills: uint32(game.Stats.DoubleKills),
		TripleKills: uint32(game.Stats.TripleKills),
		QuadraKills: uint32(game.Stats.QuadraKills),
		PentaKills: uint32(game.Stats.PentaKills),
		GoldEarned: uint32(game.Stats.GoldEarned),
		MinionsKilled: uint32(game.Stats.MinionsKilled),
		MonstersKilled: uint32(game.Stats.NeutralMinionsKilled),
		WardsPlaced: uint32(game.Stats.WardPlaced),
		WardsKilled: uint32(game.Stats.WardKilled),
		YearMonth: createYear + " " + createMonth,
		Date: createTime,
	}
}

func constructRecentGamesURL(id string, region string) string {
	return "https://" + region + ".api.pvp.net/api/lol/" + region +
		"/v1.3/game/by-summoner/" + id + "/recent?api_key=" + apiKey
}

func constructSummonerNameURL(name string, region string) string {
	return "https://" + region + ".api.pvp.net/api/lol/" + region +
		"/v1.4/summoner/by-name/" + name + "?api_key=" + apiKey
}

func constructLeagueURL(id string, region string) string {
	return "https://" + region + ".api.pvp.net/api/lol/" + region +
		"/v2.5/league/by-summoner/" + id + "/entry?api_key=" + apiKey
}

var rateLimitingBlock time.Time

func rateLimitBlock(seconds int) {
	rateLimitingBlock = time.Now().Add(time.Second * time.Duration(seconds))
}

func isRateBlocked() bool {
	return time.Now().Before(rateLimitingBlock)
}

// Form a request to Riot's APIs
func requestRiotAPI(url string) ([]byte, error) {
	// Emtpy response used for error responses
	emptyResponse := []byte{}
	for i := 0; i < 5; i++ {
		// Check if we had been blocked before for making too many requests
		if isRateBlocked() {
			printOut := "Requested reject due to rate blocking: "
			revel.WARN.Println(printOut + url)
			return emptyResponse, errors.New("Rate Limit")
		}

		// TODO Comment out in the future?
		revel.INFO.Println("Making request to Riot Games API")
		revel.INFO.Println(url)

		resp, err := http.Get(url)
		if err != nil {
			return emptyResponse, err
		}

		error1xx := resp.StatusCode < 200
		error3xx := resp.StatusCode >= 300 && resp.StatusCode < 400
		error5xx := resp.StatusCode >= 500

		// Do status code processing
		if error1xx || error3xx || error5xx {
			printOut := resp.Status + " Response from: "
			revel.ERROR.Println(printOut + url)
			if resp.StatusCode == 503 {
				printOut = "Is error 503, retrying following recommendations"
				revel.WARN.Println(printOut)
				time.Sleep(time.Second)
				continue
			} else {
				return emptyResponse, errors.New("Server Error")
			}
		}

		// API Errors handled here
		if resp.StatusCode >= 400 && resp.StatusCode < 500 {
			if resp.StatusCode == 400 {
				return emptyResponse, errors.New("Bad Request")
			} else if resp.StatusCode == 401 {
				return emptyResponse, errors.New("Unauthorized")
			} else if resp.StatusCode == 404 {
				return emptyResponse, errors.New("Not Found")
			} else if resp.StatusCode == 429 {
				printOut := "Hit maximum query limit from: "
				revel.WARN.Println(printOut + url)
				retryHeader := resp.Header.Get("Retry-After")
				retryAfter, err := strconv.Atoi(retryHeader)
				if err != nil {
					revel.ERROR.Println("Cannot determine retry after time!")
					rateLimitBlock(5)
				} else {
					rateLimitBlock(retryAfter)
				}

				return emptyResponse, errors.New("Rate Limit")
			} else {
				printOut := resp.Status + " Response from: "
				revel.ERROR.Println(printOut + url)
				return emptyResponse, errors.New("Server Error")
			}
		}

		defer resp.Body.Close()
		contents, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			printOut := "Failed to read content from: "
			revel.ERROR.Println(printOut + url)
			return emptyResponse, err
		}

		return contents, nil
	}
	return emptyResponse, errors.New("Server Error")
}

// returns id, name, error
func ResolveSummonerId(name string, region string) (string, string, error) {
	summonerNameURL := constructSummonerNameURL(name, region)
	contents, err := requestRiotAPI(summonerNameURL);

	if err != nil {
		return "", "", err
	}

	// Parse JSON data from server
	var data map[string]interface{}

	err = json.Unmarshal(contents, &data)
	if err != nil {
		printOut := "Failed to unmarshal content from: "
		revel.ERROR.Println(printOut + summonerNameURL)
		return "", "", err
	}

	rawUsername := ""
	for catchUsername, _ := range data {
		rawUsername = catchUsername
		break
	}

	if rawUsername == "" {
		revel.ERROR.Println("Failed to parse raw username")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	userData, ok := data[rawUsername].(map[string]interface{})
	if !ok {
		revel.ERROR.Println("Failed to parse data past raw username")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	rawUserId, ok := userData["id"].(float64)
	if !ok {
		revel.ERROR.Println("Failed to parse id data")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	userId := strconv.FormatFloat(rawUserId, 'f', 0, 64)

	username, ok := userData["name"].(string)
	if !ok {
		revel.ERROR.Println("Failed to parse resolved username")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	// FUCK YES! THIS ONE SHITTY REQUEST HAS PASSED THROUGH 15 CHECKS!
	return userId, username, nil
}

// Returns tier as BRONZE, SILVER, etc. and error
func GetTier(id string, region string) (string, error) {
	leagueURL := constructLeagueURL(id, region)
	contents, err := requestRiotAPI(leagueURL)
	if err != nil {
		if err.Error() == "Not Found" {
			return "UNRANKED", nil
		} else {
			return "", err
		}
	}

	var data map[string]interface{}
	err = json.Unmarshal(contents, &data)
	if err != nil {
		printOut := "Failed to unmarshal content from: "
		revel.ERROR.Println(printOut + leagueURL)
		return "", err
	}

	indexId := ""
	for catchId, _ := range data {
		indexId = catchId
		break
	}

	if indexId == "" {
		revel.ERROR.Println("Failed to parse index id")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	leagueDataArr, ok := data[indexId].([]interface{})
	if !ok {
		revel.ERROR.Println("Failed to parse league data array")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	leagueData, ok := leagueDataArr[0].(map[string]interface{})
	if !ok {
		revel.ERROR.Println("Failed to parse league data")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	tier, ok := leagueData["tier"].(string)
	if !ok {
		revel.ERROR.Println("Failed to parse tier data")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	return tier, nil
}

func GetRecentGames(id string, region string) ([]dataFormat.Game, error) {
	recentGamesURL := constructRecentGamesURL(id, region)
	contents, err := requestRiotAPI(recentGamesURL)

	if err != nil {
		return []dataFormat.Game{}, err
	}

	var gameData gameHistory
	json.Unmarshal(contents, &gameData)

	var results []dataFormat.Game

	for _, game := range gameData.Games {
		results = append(results, convertGame(game))
	}

	return results, nil
}

func LoadAPIKey() {
	var found bool
	apiKey, found = revel.Config.String("riotapikey")
	if !found {
		revel.ERROR.Println("No riotapikey field found in conf/app.conf")
		panic(errors.New("No riotapikey field found in conf/app.conf"))
	}
}
<commit_msg>Fixed open zombie connections<commit_after>// LoL Cruncher - A Historical League of Legends Statistics Tracker
// Copyright (C) 2015  Jason Chu (1lann) 1lanncontact@gmail.com

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

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

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

package riotapi

import (
	"cruncher/app/models/dataFormat"
	"encoding/json"
	"errors"
	"github.com/revel/revel"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

var apiKey string

type Player struct {
	ChampionId int
	TeamId     int
}

type Stats struct {
	GoldEarned           int
	WardPlaced           int
	WardKilled           int
	Win                  bool
	DoubleKills          int
	TripleKills          int
	QuadraKills          int
	PentaKills           int
	UnrealKills          int
	NeutralMinionsKilled int
	TimePlayed           int
	ChampionsKilled      int
	Assists              int
	NumDeaths            int
	MinionsKilled        int
}

type Game struct {
	FellowPlayers []Player
	GameType      string
	GameId        int
	TeamId        int
	GameMode      string
	ChampionId    int
	CreateDate    int64
	SubType       string
	Stats         Stats
}

type gameHistory struct {
	Games []Game
}

func convertGame(game Game) dataFormat.Game {
	isMatchedGame := (game.GameType == "MATCHED_GAME")
	isClassicGame := (game.GameMode == "CLASSIC" || game.GameMode == "ARAM")

	createTime := time.Unix(int64(game.CreateDate/1000), 0)
	createYear := strconv.Itoa(createTime.Year())
	createMonth := strconv.Itoa(int(createTime.Month()))

	return dataFormat.Game{
		DidWin:         game.Stats.Win,
		IsOnBlue:       (game.TeamId == 100),
		IsNormals:      (isMatchedGame && isClassicGame),
		ChampionId:     strconv.Itoa(game.ChampionId),
		Duration:       uint32(game.Stats.TimePlayed),
		Id:             strconv.Itoa(game.GameId),
		Type:           game.SubType,
		Kills:          uint32(game.Stats.ChampionsKilled),
		Assists:        uint32(game.Stats.Assists),
		Deaths:         uint32(game.Stats.NumDeaths),
		DoubleKills:    uint32(game.Stats.DoubleKills),
		TripleKills:    uint32(game.Stats.TripleKills),
		QuadraKills:    uint32(game.Stats.QuadraKills),
		PentaKills:     uint32(game.Stats.PentaKills),
		GoldEarned:     uint32(game.Stats.GoldEarned),
		MinionsKilled:  uint32(game.Stats.MinionsKilled),
		MonstersKilled: uint32(game.Stats.NeutralMinionsKilled),
		WardsPlaced:    uint32(game.Stats.WardPlaced),
		WardsKilled:    uint32(game.Stats.WardKilled),
		YearMonth:      createYear + " " + createMonth,
		Date:           createTime,
	}
}

func constructRecentGamesURL(id string, region string) string {
	return "https://" + region + ".api.pvp.net/api/lol/" + region +
		"/v1.3/game/by-summoner/" + id + "/recent?api_key=" + apiKey
}

func constructSummonerNameURL(name string, region string) string {
	return "https://" + region + ".api.pvp.net/api/lol/" + region +
		"/v1.4/summoner/by-name/" + name + "?api_key=" + apiKey
}

func constructLeagueURL(id string, region string) string {
	return "https://" + region + ".api.pvp.net/api/lol/" + region +
		"/v2.5/league/by-summoner/" + id + "/entry?api_key=" + apiKey
}

var rateLimitingBlock time.Time

func rateLimitBlock(seconds int) {
	rateLimitingBlock = time.Now().Add(time.Second * time.Duration(seconds))
}

func isRateBlocked() bool {
	return time.Now().Before(rateLimitingBlock)
}

// Form a request to Riot's APIs
func requestRiotAPI(url string) ([]byte, error) {
	// Emtpy response used for error responses
	emptyResponse := []byte{}
	for i := 0; i < 5; i++ {
		// Check if we had been blocked before for making too many requests
		if isRateBlocked() {
			printOut := "Requested reject due to rate blocking: "
			revel.WARN.Println(printOut + url)
			return emptyResponse, errors.New("Rate Limit")
		}

		// TODO Comment out in the future?
		revel.INFO.Println("Making request to Riot Games API")
		revel.INFO.Println(url)

		resp, err := http.Get(url)
		if err != nil {
			return emptyResponse, err
		}

		defer resp.Body.Close()

		error1xx := resp.StatusCode < 200
		error3xx := resp.StatusCode >= 300 && resp.StatusCode < 400
		error5xx := resp.StatusCode >= 500

		// Do status code processing
		if error1xx || error3xx || error5xx {
			printOut := resp.Status + " Response from: "
			revel.ERROR.Println(printOut + url)
			if resp.StatusCode == 503 {
				printOut = "Is error 503, retrying following recommendations"
				revel.WARN.Println(printOut)
				time.Sleep(time.Second)
				continue
			} else {
				return emptyResponse, errors.New("Server Error")
			}
		}

		// API Errors handled here
		if resp.StatusCode >= 400 && resp.StatusCode < 500 {
			if resp.StatusCode == 400 {
				return emptyResponse, errors.New("Bad Request")
			} else if resp.StatusCode == 401 {
				return emptyResponse, errors.New("Unauthorized")
			} else if resp.StatusCode == 404 {
				return emptyResponse, errors.New("Not Found")
			} else if resp.StatusCode == 429 {
				printOut := "Hit maximum query limit from: "
				revel.WARN.Println(printOut + url)
				retryHeader := resp.Header.Get("Retry-After")
				retryAfter, err := strconv.Atoi(retryHeader)
				if err != nil {
					revel.ERROR.Println("Cannot determine retry after time!")
					rateLimitBlock(5)
				} else {
					rateLimitBlock(retryAfter)
				}

				return emptyResponse, errors.New("Rate Limit")
			} else {
				printOut := resp.Status + " Response from: "
				revel.ERROR.Println(printOut + url)
				return emptyResponse, errors.New("Server Error")
			}
		}

		contents, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			printOut := "Failed to read content from: "
			revel.ERROR.Println(printOut + url)
			return emptyResponse, err
		}

		return contents, nil
	}
	return emptyResponse, errors.New("Server Error")
}

// returns id, name, error
func ResolveSummonerId(name string, region string) (string, string, error) {
	summonerNameURL := constructSummonerNameURL(name, region)
	contents, err := requestRiotAPI(summonerNameURL)

	if err != nil {
		return "", "", err
	}

	// Parse JSON data from server
	var data map[string]interface{}

	err = json.Unmarshal(contents, &data)
	if err != nil {
		printOut := "Failed to unmarshal content from: "
		revel.ERROR.Println(printOut + summonerNameURL)
		return "", "", err
	}

	rawUsername := ""
	for catchUsername, _ := range data {
		rawUsername = catchUsername
		break
	}

	if rawUsername == "" {
		revel.ERROR.Println("Failed to parse raw username")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	userData, ok := data[rawUsername].(map[string]interface{})
	if !ok {
		revel.ERROR.Println("Failed to parse data past raw username")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	rawUserId, ok := userData["id"].(float64)
	if !ok {
		revel.ERROR.Println("Failed to parse id data")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	userId := strconv.FormatFloat(rawUserId, 'f', 0, 64)

	username, ok := userData["name"].(string)
	if !ok {
		revel.ERROR.Println("Failed to parse resolved username")
		revel.ERROR.Println(data)
		return "", "", errors.New("Data parse error")
	}

	// FUCK YES! THIS ONE SHITTY REQUEST HAS PASSED THROUGH 15 CHECKS!
	return userId, username, nil
}

// Returns tier as BRONZE, SILVER, etc. and error
func GetTier(id string, region string) (string, error) {
	leagueURL := constructLeagueURL(id, region)
	contents, err := requestRiotAPI(leagueURL)
	if err != nil {
		if err.Error() == "Not Found" {
			return "UNRANKED", nil
		} else {
			return "", err
		}
	}

	var data map[string]interface{}
	err = json.Unmarshal(contents, &data)
	if err != nil {
		printOut := "Failed to unmarshal content from: "
		revel.ERROR.Println(printOut + leagueURL)
		return "", err
	}

	indexId := ""
	for catchId, _ := range data {
		indexId = catchId
		break
	}

	if indexId == "" {
		revel.ERROR.Println("Failed to parse index id")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	leagueDataArr, ok := data[indexId].([]interface{})
	if !ok {
		revel.ERROR.Println("Failed to parse league data array")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	leagueData, ok := leagueDataArr[0].(map[string]interface{})
	if !ok {
		revel.ERROR.Println("Failed to parse league data")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	tier, ok := leagueData["tier"].(string)
	if !ok {
		revel.ERROR.Println("Failed to parse tier data")
		revel.ERROR.Println(data)
		return "", errors.New("Data parse error")
	}

	return tier, nil
}

func GetRecentGames(id string, region string) ([]dataFormat.Game, error) {
	recentGamesURL := constructRecentGamesURL(id, region)
	contents, err := requestRiotAPI(recentGamesURL)

	if err != nil {
		return []dataFormat.Game{}, err
	}

	var gameData gameHistory
	json.Unmarshal(contents, &gameData)

	var results []dataFormat.Game

	for _, game := range gameData.Games {
		results = append(results, convertGame(game))
	}

	return results, nil
}

func LoadAPIKey() {
	var found bool
	apiKey, found = revel.Config.String("riotapikey")
	if !found {
		revel.ERROR.Println("No riotapikey field found in conf/app.conf")
		panic(errors.New("No riotapikey field found in conf/app.conf"))
	}
}
<|endoftext|>