<commit_before>package autoca

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io/ioutil"
	"log"
	"math/big"
	"os"
	"strconv"
	"time"
)

var LOGGER = log.New(os.Stdout, "AutoCA ", log.Ldate|log.Ltime|log.Lshortfile)

type AutoCA struct {
	cert       *x509.Certificate
	privateKey *rsa.PrivateKey
	serialDB   string
}

func (ca *AutoCA) Init(certFile string, keyFile string, pass string, db string) error {

	data, err := ioutil.ReadFile(certFile)
	if err != nil {
		LOGGER.Printf("Failed to read certificate file: %v\n", err)
		return err
	}

	c, _ := pem.Decode(data)
	ca.cert, err = x509.ParseCertificate(c.Bytes)
	if err != nil {
		LOGGER.Printf("Failed to decode certificate: %v\n", err)
		return err
	}

	data, err = ioutil.ReadFile(keyFile)
	if err != nil {
		LOGGER.Printf("Failed to read key file: %v\n", err)
		return err
	}

	k, _ := pem.Decode(data)
	key, err := x509.DecryptPEMBlock(k, []byte(pass))
	if err != nil {
		LOGGER.Printf("Failed to decrypt key: %v\n", err)
		return err
	}

	ca.privateKey, err = x509.ParsePKCS1PrivateKey(key)
	if err != nil {
		LOGGER.Printf("Failed to decode key: %v\n", err)
		return err
	}
	ca.serialDB = db
	return nil
}

func (ca *AutoCA) GetHostCertificateTemplate(dn []string, notBefore time.Time, notAfter time.Time) *x509.Certificate {

	template := &x509.Certificate{
		IsCA: false,
		BasicConstraintsValid: true,
		SerialNumber:          big.NewInt(ca.nextSerial()),
		Subject: pkix.Name{
			Country:            ca.cert.Subject.Country,
			Organization:       ca.cert.Subject.Organization,
			OrganizationalUnit: ca.cert.Subject.OrganizationalUnit,
			Locality:           ca.cert.Subject.Locality,
			Province:           ca.cert.Subject.Province,
			StreetAddress:      ca.cert.Subject.StreetAddress,
			PostalCode:         ca.cert.Subject.PostalCode,
			CommonName:         dn[0],
		},
		NotBefore:   notBefore,
		NotAfter:    notAfter,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		DNSNames:    dn[1:],
	}

	return template
}

func (ca *AutoCA) CreateCertificate(template *x509.Certificate, publicKey *rsa.PublicKey) ([]byte, error) {

	cert, err := x509.CreateCertificate(rand.Reader, template, ca.cert, publicKey, ca.privateKey)
	if err != nil {
		LOGGER.Printf("Failed to create certificate: %v\n", err)
		return nil, err
	}

	return cert, nil

}

func (ca *AutoCA) nextSerial() int64 {

	var serial int64
	data, err := ioutil.ReadFile(ca.serialDB)
	if err != nil && !os.IsNotExist(err) {
		LOGGER.Printf("Failed to read serial: %v\n", err)
	} else {
		serial, err = strconv.ParseInt(string(data), 10, 64)
	}

	serial++
	err = ioutil.WriteFile(ca.serialDB, []byte(strconv.FormatInt(serial, 10)), 0644)
	if err != nil {
		LOGGER.Printf("Failed to write new serial into %s : %v\n", ca.serialDB, err)
	}
	return serial

}
<commit_msg>ca: protect serial db file with lock<commit_after>package autoca

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"crypto/x509/pkix"
	"encoding/pem"
	"io/ioutil"
	"log"
	"math/big"
	"os"
	"strconv"
	"sync"
	"time"
)

var LOGGER = log.New(os.Stdout, "AutoCA ", log.Ldate|log.Ltime|log.Lshortfile)

type AutoCA struct {
	cert         *x509.Certificate
	privateKey   *rsa.PrivateKey
	serialDB     string
	serialDBLock sync.Mutex
}

func (ca *AutoCA) Init(certFile string, keyFile string, pass string, db string) error {

	data, err := ioutil.ReadFile(certFile)
	if err != nil {
		LOGGER.Printf("Failed to read certificate file: %v\n", err)
		return err
	}

	c, _ := pem.Decode(data)
	ca.cert, err = x509.ParseCertificate(c.Bytes)
	if err != nil {
		LOGGER.Printf("Failed to decode certificate: %v\n", err)
		return err
	}

	data, err = ioutil.ReadFile(keyFile)
	if err != nil {
		LOGGER.Printf("Failed to read key file: %v\n", err)
		return err
	}

	k, _ := pem.Decode(data)
	key, err := x509.DecryptPEMBlock(k, []byte(pass))
	if err != nil {
		LOGGER.Printf("Failed to decrypt key: %v\n", err)
		return err
	}

	ca.privateKey, err = x509.ParsePKCS1PrivateKey(key)
	if err != nil {
		LOGGER.Printf("Failed to decode key: %v\n", err)
		return err
	}
	ca.serialDB = db
	return nil
}

func (ca *AutoCA) GetHostCertificateTemplate(dn []string, notBefore time.Time, notAfter time.Time) *x509.Certificate {

	template := &x509.Certificate{
		IsCA: false,
		BasicConstraintsValid: true,
		SerialNumber:          big.NewInt(ca.nextSerial()),
		Subject: pkix.Name{
			Country:            ca.cert.Subject.Country,
			Organization:       ca.cert.Subject.Organization,
			OrganizationalUnit: ca.cert.Subject.OrganizationalUnit,
			Locality:           ca.cert.Subject.Locality,
			Province:           ca.cert.Subject.Province,
			StreetAddress:      ca.cert.Subject.StreetAddress,
			PostalCode:         ca.cert.Subject.PostalCode,
			CommonName:         dn[0],
		},
		NotBefore:   notBefore,
		NotAfter:    notAfter,
		ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
		KeyUsage:    x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		DNSNames:    dn[1:],
	}

	return template
}

func (ca *AutoCA) CreateCertificate(template *x509.Certificate, publicKey *rsa.PublicKey) ([]byte, error) {

	cert, err := x509.CreateCertificate(rand.Reader, template, ca.cert, publicKey, ca.privateKey)
	if err != nil {
		LOGGER.Printf("Failed to create certificate: %v\n", err)
		return nil, err
	}

	return cert, nil

}

func (ca *AutoCA) nextSerial() int64 {

	ca.serialDBLock.Lock()
	var serial int64
	data, err := ioutil.ReadFile(ca.serialDB)
	if err != nil && !os.IsNotExist(err) {
		LOGGER.Printf("Failed to read serial: %v\n", err)
	} else {
		serial, err = strconv.ParseInt(string(data), 10, 64)
	}

	serial++
	err = ioutil.WriteFile(ca.serialDB, []byte(strconv.FormatInt(serial, 10)), 0644)
	if err != nil {
		LOGGER.Printf("Failed to write new serial into %s : %v\n", ca.serialDB, err)
	}
	ca.serialDBLock.Unlock()
	return serial

}
<|endoftext|>


<commit_before>package db

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/golang/glog"
	_ "github.com/mattn/go-sqlite3"
	"skia.googlesource.com/buildbot.git/go/metadata"
	"skia.googlesource.com/buildbot.git/go/util"
)

var (
	// DB is the sql database where we have commit and annotation information stored.
	DB *sql.DB = nil

	// Keeps track if we are connected to MySQL or SQLite
	isMySQL = true
)

const (
	// Key of the password for the readwrite user.
	METADATA_KEY = "readwrite"

	// Path where the SQLite database is stored when running locally.
	SQLITE_DB_PATH = "./perf.db"

	// Template to generate the database connection string in production.
	// The IP address of the database is found here:
	//    https://console.developers.google.com/project/31977622648/sql/instances/skiaperf/overview
	// And 3306 is the default port for MySQL.
	DB_CONN_TMPL = "readwrite:%s@tcp(173.194.104.24:3306)/skia?parseTime=true"
)

// Init must be called once before DB is used.
//
// Since it used glog, make sure it is also called after flag.Parse is called.
func Init(mysqlConnStr string) {
	// If there is a connection string then connect to the MySQL server.
	// This is for testing only. In production we get the relevant information
	// from the metadata server.
	var err error

	if mysqlConnStr != "" {
		glog.Infoln("Opening SQL database.")
		if DB, err = sql.Open("mysql", mysqlConnStr); err == nil {
			glog.Infoln("Sending Ping.")
			err = DB.Ping()
		}

		if err != nil {
			glog.Fatalln("Failed to open connection to SQL server:", err)
		}

		isMySQL = true
	} else {
		// Open a local SQLite database instead.
		glog.Infof("Opening local sqlite database at: %s", SQLITE_DB_PATH)
		// Fallback to sqlite for local use.
		DB, err = sql.Open("sqlite3", SQLITE_DB_PATH)
		if err != nil {
			glog.Fatalln("Failed to open:", err)
		}
		isMySQL = false
	}

	// Make sure the migration table exists.
	if err := checkVersionTable(); err != nil {
		// We are using panic() instead of Fataln() to be able to trap this
		// in tests and make sure it fails when no version table exists.
		glog.Errorln("Unable to create version table.")
		panic("Attempt to create version table returned: " + err.Error())
	}
	glog.Infoln("Version table OK.")

	// Ping the database to keep the connection fresh.
	go func() {
		c := time.Tick(1 * time.Minute)
		for _ = range c {
			if err := DB.Ping(); err != nil {
				glog.Warningln("Database failed to respond:", err)
			}
			glog.Infof("db: Successful ping")
		}
	}()
}

// Returns the DB connection string for running in production where a
// metadata server is available. If 'local' is true it will always return
// "" (empty string). When used with Init() this will cause it to use a
// local SQLite database. If it's not local and the meta data server is
// unreachable it will terminate.
func ProdConnectionString(local bool) string {
	if local {
		return ""
	}

	//  First, get the password from the metadata server.
	// See https://developers.google.com/compute/docs/metadata#custom.
	password, err := metadata.Get(METADATA_KEY)
	if err != nil {
		glog.Fatalf("Failed to find metadata. Use 'local' flag when running locally.")
	}
	return fmt.Sprintf(DB_CONN_TMPL, password)
}

// Migrates the database to the specified target version. Use DBVersion() to
// retrieve the current version of the database.
func Migrate(targetVersion int) error {
	if (targetVersion < 0) || (targetVersion > MaxDBVersion()) {
		glog.Fatalf("Target db version must be in range: [0 .. %d]", MaxDBVersion())
	}

	currentVersion, err := DBVersion()
	if err != nil {
		return err
	}

	if targetVersion == currentVersion {
		return nil
	}

	// start a transaction
	txn, err := DB.Begin()
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			glog.Errorf("Rolling back comit. Error: %s", err)
			txn.Rollback()
		} else {
			glog.Infoln("Committing changes.")
			txn.Commit()
		}
	}()

	// run through the transactions
	runSteps := getMigrations(isMySQL, currentVersion, targetVersion)
	if len(runSteps) == 0 {
		glog.Fatalln("Unable to find migration steps.")
	}

	for _, step := range runSteps {
		for _, stmt := range step {
			glog.Infoln("EXECUTING: \n", stmt)
			if _, err = txn.Exec(stmt); err != nil {
				return err
			}
		}
	}

	// update the dbversion table
	if err = setDBVersion(txn, targetVersion); err != nil {
		return err
	}

	return nil
}

// Returns the current version of the database. It assumes that the
// Migrate function has already been called and the version table has been
// created in the database.
func DBVersion() (int, error) {
	stmt := `
		SELECT version
		FROM sk_db_version
		WHERE id=1`

	var version int
	err := DB.QueryRow(stmt).Scan(&version)
	return version, err
}

// Returns the highest version currently available.
func MaxDBVersion() int {
	return len(migrationSteps)
}

// Returns an error if the version table does not exist.
func checkVersionTable() error {
	// Check if the table exists in MySQL or SQLite.
	stmt := "SHOW TABLES LIKE 'sk_db_version'"
	if !isMySQL {
		stmt = "SELECT name FROM sqlite_master WHERE type='table' AND name='sk_db_version';"
	}

	var temp string
	err := DB.QueryRow(stmt).Scan(&temp)
	if err != nil {
		// See if we can create the version table.
		return ensureVersionTable()
	}

	return nil
}

func setDBVersion(txn *sql.Tx, newDBVersion int) error {
	stmt := `REPLACE INTO sk_db_version (id, version, updated) VALUES(1, ?, ?)`
	_, err := txn.Exec(stmt, newDBVersion, time.Now().Unix())
	return err
}

func ensureVersionTable() error {
	txn, err := DB.Begin()
	defer func() {
		if err != nil {
			glog.Errorf("Encountered error rolling back: %s", err.Error())
			txn.Rollback()
		} else {
			txn.Commit()
		}
	}()

	if err != nil {
		fmt.Errorf("Unable to start database transaction. %s", err.Error())
	}

	stmt := `CREATE TABLE IF NOT EXISTS sk_db_version (
			id         INTEGER      NOT NULL PRIMARY KEY,
			version    INTEGER      NOT NULL,
			updated    BIGINT       NOT NULL
		)`
	if _, err = txn.Exec(stmt); err != nil {
		return fmt.Errorf("Creating version table failed: %s", err.Error())
	}

	stmt = "SELECT COUNT(*) FROM sk_db_version"
	var count int
	if err = txn.QueryRow(stmt).Scan(&count); err != nil {
		return fmt.Errorf("Unable to read version table: %s", err.Error())
	}

	// In both cases we want the transaction to roll back.
	if count == 0 {
		err = setDBVersion(txn, 0)
	} else if count > 1 {
		err = fmt.Errorf("Version table contains more than one row.")
	}

	return err
}

// Returns the SQL statements base on whether we are using MySQL and the
// current and target DB version.
// This function assumes that currentVersion != targetVersion.
func getMigrations(isMySQL bool, currentVersion int, targetVersion int) [][]string {
	inc := util.SignInt(targetVersion - currentVersion)
	idx := currentVersion
	if inc < 0 {
		idx = currentVersion - 1
	}
	delta := util.AbsInt(targetVersion - currentVersion)
	result := make([][]string, 0, delta)

	for i := 0; i < delta; i++ {
		var temp []string
		switch {
		// using mysqlp
		case (inc > 0) && isMySQL:
			temp = migrationSteps[idx].MySQLUp
		case (inc < 0) && isMySQL:
			temp = migrationSteps[idx].MySQLDown
		// using sqlite
		case (inc > 0):
			temp = migrationSteps[idx].SQLiteUp
		case (inc < 0):
			temp = migrationSteps[idx].SQLiteDown
		}
		result = append(result, temp)
		idx += inc
	}
	return result
}

// Define the migration steps.
// Note: Only add to this list, once a step has landed in version control it
// must not be changed.
var migrationSteps = []struct {
	MySQLUp    []string
	MySQLDown  []string
	SQLiteUp   []string
	SQLiteDown []string
}{
	// version 1
	{
		MySQLUp: []string{
			`CREATE TABLE IF NOT EXISTS shortcuts (
				id      INT             NOT NULL AUTO_INCREMENT PRIMARY KEY,
				traces  MEDIUMTEXT      NOT NULL
			)`,

			`CREATE TABLE IF NOT EXISTS clusters (
				id         INT          NOT NULL AUTO_INCREMENT PRIMARY KEY,
				ts         BIGINT       NOT NULL,
				hash       TEXT         NOT NULL,
				regression FLOAT        NOT NULL,
				cluster    MEDIUMTEXT   NOT NULL,
				status     TEXT         NOT NULL,
				message    TEXT         NOT NULL
			)`,

			`CREATE TABLE IF NOT EXISTS tries (
				issue       VARCHAR(255) NOT NULL PRIMARY KEY,
				lastUpdated BIGINT       NOT NULL,
				results     LONGTEXT   NOT NULL
			)`,
		},
		MySQLDown: []string{},
		SQLiteUp: []string{
			`CREATE TABLE clusters (
				id         INTEGER      NOT NULL PRIMARY KEY AUTOINCREMENT,
				ts         TIMESTAMP    NOT NULL,
				hash       TEXT         NOT NULL,
				regression FLOAT        NOT NULL,
				cluster    MEDIUMTEXT   NOT NULL,
				status     TEXT         NOT NULL,
				message    TEXT         NOT NULL
			)`,
			`CREATE TABLE shortcuts (
				id      INTEGER     NOT NULL PRIMARY KEY AUTOINCREMENT,
				traces  MEDIUMTEXT  NOT NULL
			)`,
			`CREATE TABLE tries (
				issue       VARCHAR(255) NOT NULL PRIMARY KEY,
				lastUpdated TIMESTAMP    NOT NULL,
				results     MEDIUMTEXT   NOT NULL
			)`,
		},
		SQLiteDown: []string{
			`DROP TABLE IF EXISTS clusters`,
			`DROP TABLE IF EXISTS shortcuts`,
			`DROP TABLE IF EXISTS tries`,
		},
	},
	// version 2
	{
		MySQLUp: []string{
			`CREATE TABLE IF NOT EXISTS activitylog (
				id         INT          NOT NULL AUTO_INCREMENT PRIMARY KEY,
				timestamp  BIGINT       NOT NULL,
				userid     TEXT         NOT NULL,
				action     TEXT         NOT NULL,
				url        TEXT
			)`,
		},
		MySQLDown: []string{},
		SQLiteUp: []string{
			`CREATE TABLE activitylog (
				id         INTEGER      NOT NULL PRIMARY KEY AUTOINCREMENT,
				timestamp  TIMESTAMP    NOT NULL,
				userid     TEXT         NOT NULL,
				action     TEXT         NOT NULL,
				url        TEXT
			)`,
		},
		SQLiteDown: []string{
			`DROP TABLE IF EXISTS activitylog`,
		},
	},

	// Use this is a template for more migration steps.
	// version x
	// {
	// 	MySQLUp: ,
	// 	MySQLDown: ,
	// 	SQLiteUp: ,
	// 	SQLiteDown: ,
	// },
}
<commit_msg>Automatically upgrades the database when using SQLite. This makes testing easier and with SQLite we are not concerned with permissions. (MySQL requires a different user for creating/deleting/altering tables).<commit_after>package db

import (
	"database/sql"
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/golang/glog"
	_ "github.com/mattn/go-sqlite3"
	"skia.googlesource.com/buildbot.git/go/metadata"
	"skia.googlesource.com/buildbot.git/go/util"
)

var (
	// DB is the sql database where we have commit and annotation information stored.
	DB *sql.DB = nil

	// Keeps track if we are connected to MySQL or SQLite
	isMySQL = true
)

const (
	// Key of the password for the readwrite user.
	METADATA_KEY = "readwrite"

	// Path where the SQLite database is stored when running locally.
	SQLITE_DB_PATH = "./perf.db"

	// Template to generate the database connection string in production.
	// The IP address of the database is found here:
	//    https://console.developers.google.com/project/31977622648/sql/instances/skiaperf/overview
	// And 3306 is the default port for MySQL.
	DB_CONN_TMPL = "readwrite:%s@tcp(173.194.104.24:3306)/skia?parseTime=true"
)

// Init must be called once before DB is used.
//
// Since it used glog, make sure it is also called after flag.Parse is called.
func Init(mysqlConnStr string) {
	// If there is a connection string then connect to the MySQL server.
	// This is for testing only. In production we get the relevant information
	// from the metadata server.
	var err error

	if mysqlConnStr != "" {
		glog.Infoln("Opening SQL database.")
		if DB, err = sql.Open("mysql", mysqlConnStr); err == nil {
			glog.Infoln("Sending Ping.")
			err = DB.Ping()
		}

		if err != nil {
			glog.Fatalln("Failed to open connection to SQL server:", err)
		}

		isMySQL = true
	} else {
		// Open a local SQLite database instead.
		glog.Infof("Opening local sqlite database at: %s", SQLITE_DB_PATH)
		// Fallback to sqlite for local use.
		DB, err = sql.Open("sqlite3", SQLITE_DB_PATH)
		if err != nil {
			glog.Fatalln("Failed to open:", err)
		}
		isMySQL = false
	}

	// Make sure the migration table exists.
	if err := checkVersionTable(); err != nil {
		// We are using panic() instead of Fataln() to be able to trap this
		// in tests and make sure it fails when no version table exists.
		glog.Errorln("Unable to create version table.")
		panic("Attempt to create version table returned: " + err.Error())
	}
	glog.Infoln("Version table OK.")

	// Migrate to the latest version if we are using SQLite, so we don't have
	// to run the migratdb command for a local database.
	if !isMySQL {
		Migrate(MaxDBVersion())
	}

	// Ping the database to keep the connection fresh.
	go func() {
		c := time.Tick(1 * time.Minute)
		for _ = range c {
			if err := DB.Ping(); err != nil {
				glog.Warningln("Database failed to respond:", err)
			}
			glog.Infof("db: Successful ping")
		}
	}()
}

// Returns the DB connection string for running in production where a
// metadata server is available. If 'local' is true it will always return
// "" (empty string). When used with Init() this will cause it to use a
// local SQLite database. If it's not local and the meta data server is
// unreachable it will terminate.
func ProdConnectionString(local bool) string {
	if local {
		return ""
	}

	//  First, get the password from the metadata server.
	// See https://developers.google.com/compute/docs/metadata#custom.
	password, err := metadata.Get(METADATA_KEY)
	if err != nil {
		glog.Fatalf("Failed to find metadata. Use 'local' flag when running locally.")
	}
	return fmt.Sprintf(DB_CONN_TMPL, password)
}

// Migrates the database to the specified target version. Use DBVersion() to
// retrieve the current version of the database.
func Migrate(targetVersion int) error {
	if (targetVersion < 0) || (targetVersion > MaxDBVersion()) {
		glog.Fatalf("Target db version must be in range: [0 .. %d]", MaxDBVersion())
	}

	currentVersion, err := DBVersion()
	if err != nil {
		return err
	}

	if targetVersion == currentVersion {
		return nil
	}

	// start a transaction
	txn, err := DB.Begin()
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			glog.Errorf("Rolling back comit. Error: %s", err)
			txn.Rollback()
		} else {
			glog.Infoln("Committing changes.")
			txn.Commit()
		}
	}()

	// run through the transactions
	runSteps := getMigrations(isMySQL, currentVersion, targetVersion)
	if len(runSteps) == 0 {
		glog.Fatalln("Unable to find migration steps.")
	}

	for _, step := range runSteps {
		for _, stmt := range step {
			glog.Infoln("EXECUTING: \n", stmt)
			if _, err = txn.Exec(stmt); err != nil {
				return err
			}
		}
	}

	// update the dbversion table
	if err = setDBVersion(txn, targetVersion); err != nil {
		return err
	}

	return nil
}

// Returns the current version of the database. It assumes that the
// Migrate function has already been called and the version table has been
// created in the database.
func DBVersion() (int, error) {
	stmt := `
		SELECT version
		FROM sk_db_version
		WHERE id=1`

	var version int
	err := DB.QueryRow(stmt).Scan(&version)
	return version, err
}

// Returns the highest version currently available.
func MaxDBVersion() int {
	return len(migrationSteps)
}

// Returns an error if the version table does not exist.
func checkVersionTable() error {
	// Check if the table exists in MySQL or SQLite.
	stmt := "SHOW TABLES LIKE 'sk_db_version'"
	if !isMySQL {
		stmt = "SELECT name FROM sqlite_master WHERE type='table' AND name='sk_db_version';"
	}

	var temp string
	err := DB.QueryRow(stmt).Scan(&temp)
	if err != nil {
		// See if we can create the version table.
		return ensureVersionTable()
	}

	return nil
}

func setDBVersion(txn *sql.Tx, newDBVersion int) error {
	stmt := `REPLACE INTO sk_db_version (id, version, updated) VALUES(1, ?, ?)`
	_, err := txn.Exec(stmt, newDBVersion, time.Now().Unix())
	return err
}

func ensureVersionTable() error {
	txn, err := DB.Begin()
	defer func() {
		if err != nil {
			glog.Errorf("Encountered error rolling back: %s", err.Error())
			txn.Rollback()
		} else {
			txn.Commit()
		}
	}()

	if err != nil {
		fmt.Errorf("Unable to start database transaction. %s", err.Error())
	}

	stmt := `CREATE TABLE IF NOT EXISTS sk_db_version (
			id         INTEGER      NOT NULL PRIMARY KEY,
			version    INTEGER      NOT NULL,
			updated    BIGINT       NOT NULL
		)`
	if _, err = txn.Exec(stmt); err != nil {
		return fmt.Errorf("Creating version table failed: %s", err.Error())
	}

	stmt = "SELECT COUNT(*) FROM sk_db_version"
	var count int
	if err = txn.QueryRow(stmt).Scan(&count); err != nil {
		return fmt.Errorf("Unable to read version table: %s", err.Error())
	}

	// In both cases we want the transaction to roll back.
	if count == 0 {
		err = setDBVersion(txn, 0)
	} else if count > 1 {
		err = fmt.Errorf("Version table contains more than one row.")
	}

	return err
}

// Returns the SQL statements base on whether we are using MySQL and the
// current and target DB version.
// This function assumes that currentVersion != targetVersion.
func getMigrations(isMySQL bool, currentVersion int, targetVersion int) [][]string {
	inc := util.SignInt(targetVersion - currentVersion)
	idx := currentVersion
	if inc < 0 {
		idx = currentVersion - 1
	}
	delta := util.AbsInt(targetVersion - currentVersion)
	result := make([][]string, 0, delta)

	for i := 0; i < delta; i++ {
		var temp []string
		switch {
		// using mysqlp
		case (inc > 0) && isMySQL:
			temp = migrationSteps[idx].MySQLUp
		case (inc < 0) && isMySQL:
			temp = migrationSteps[idx].MySQLDown
		// using sqlite
		case (inc > 0):
			temp = migrationSteps[idx].SQLiteUp
		case (inc < 0):
			temp = migrationSteps[idx].SQLiteDown
		}
		result = append(result, temp)
		idx += inc
	}
	return result
}

// Define the migration steps.
// Note: Only add to this list, once a step has landed in version control it
// must not be changed.
var migrationSteps = []struct {
	MySQLUp    []string
	MySQLDown  []string
	SQLiteUp   []string
	SQLiteDown []string
}{
	// version 1
	{
		MySQLUp: []string{
			`CREATE TABLE IF NOT EXISTS shortcuts (
				id      INT             NOT NULL AUTO_INCREMENT PRIMARY KEY,
				traces  MEDIUMTEXT      NOT NULL
			)`,

			`CREATE TABLE IF NOT EXISTS clusters (
				id         INT          NOT NULL AUTO_INCREMENT PRIMARY KEY,
				ts         BIGINT       NOT NULL,
				hash       TEXT         NOT NULL,
				regression FLOAT        NOT NULL,
				cluster    MEDIUMTEXT   NOT NULL,
				status     TEXT         NOT NULL,
				message    TEXT         NOT NULL
			)`,

			`CREATE TABLE IF NOT EXISTS tries (
				issue       VARCHAR(255) NOT NULL PRIMARY KEY,
				lastUpdated BIGINT       NOT NULL,
				results     LONGTEXT   NOT NULL
			)`,
		},
		MySQLDown: []string{},
		SQLiteUp: []string{
			`CREATE TABLE clusters (
				id         INTEGER      NOT NULL PRIMARY KEY AUTOINCREMENT,
				ts         TIMESTAMP    NOT NULL,
				hash       TEXT         NOT NULL,
				regression FLOAT        NOT NULL,
				cluster    MEDIUMTEXT   NOT NULL,
				status     TEXT         NOT NULL,
				message    TEXT         NOT NULL
			)`,
			`CREATE TABLE shortcuts (
				id      INTEGER     NOT NULL PRIMARY KEY AUTOINCREMENT,
				traces  MEDIUMTEXT  NOT NULL
			)`,
			`CREATE TABLE tries (
				issue       VARCHAR(255) NOT NULL PRIMARY KEY,
				lastUpdated TIMESTAMP    NOT NULL,
				results     MEDIUMTEXT   NOT NULL
			)`,
		},
		SQLiteDown: []string{
			`DROP TABLE IF EXISTS clusters`,
			`DROP TABLE IF EXISTS shortcuts`,
			`DROP TABLE IF EXISTS tries`,
		},
	},
	// version 2
	{
		MySQLUp: []string{
			`CREATE TABLE IF NOT EXISTS activitylog (
				id         INT          NOT NULL AUTO_INCREMENT PRIMARY KEY,
				timestamp  BIGINT       NOT NULL,
				userid     TEXT         NOT NULL,
				action     TEXT         NOT NULL,
				url        TEXT
			)`,
		},
		MySQLDown: []string{},
		SQLiteUp: []string{
			`CREATE TABLE activitylog (
				id         INTEGER      NOT NULL PRIMARY KEY AUTOINCREMENT,
				timestamp  TIMESTAMP    NOT NULL,
				userid     TEXT         NOT NULL,
				action     TEXT         NOT NULL,
				url        TEXT
			)`,
		},
		SQLiteDown: []string{
			`DROP TABLE IF EXISTS activitylog`,
		},
	},

	// Use this is a template for more migration steps.
	// version x
	// {
	// 	MySQLUp: ,
	// 	MySQLDown: ,
	// 	SQLiteUp: ,
	// 	SQLiteDown: ,
	// },
}
<|endoftext|>


<commit_before>package goheroku

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"github.com/Shopify/sarama"
	cluster "github.com/bsm/sarama-cluster"
	"net/url"
	"os"
	"strings"
)

//  NewClusterConsumer creates a bsm sarama-cluster consumer
//  Provide the topic, consumer group and a cluster config
func NewClusterConsumer(topic string, consumerGroup string, cfg *cluster.Config) (*cluster.Consumer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	topic = AppendPrefixTo(topic)
	group := AppendPrefixTo(consumerGroup)
	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}

	consumer, err := cluster.NewConsumer(brokers, group, []string{topic}, cfg)
	if err != nil {
		return nil, err
	}

	return consumer, nil
}

// TODO: Investigate use of/need for topic
func NewConsumer(cfg *sarama.Config) (sarama.Consumer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	consumer, err := sarama.NewConsumer(brokers, cfg)
	if err != nil {
		return nil, err
	}

	return consumer, nil
}

//  NewAsyncProducer creates a sarama Async Producer
//  Provide a sarama config
//  For more information about the difference between Async and Sync producers
//  see the sarama documentation
func NewAsyncProducer(cfg *sarama.Config) (sarama.AsyncProducer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	producer, err := sarama.NewAsyncProducer(brokers, cfg)
	if err != nil {
		return nil, err
	}

	return producer, nil
}

//  NewSyncProducer creates a sarama Sync Producer
//  Provide a sarama config
//  For more information about the difference between Async and Sync producers
//  see the sarama documentation
func NewSyncProducer(cfg *sarama.Config) (sarama.SyncProducer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	producer, err := sarama.NewSyncProducer(brokers, cfg)
	if err != nil {
		return nil, err
	}

	return producer, nil
}

// Endangered function. TODO: Investigate usefulness of client
func NewClient(cfg *sarama.Config) (sarama.Client, error) {
	addrs, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	client, err := sarama.NewClient(addrs, cfg)
	if err != nil {
		return nil, err
	}
	return client, nil
}

// To specify the topic or consumer group, the Kafka prefix needs
// to appended to it. This function makes it possible without having
// access to the app config.
func AppendPrefixTo(str string) string {
	prefix := os.Getenv("KAFKA_PREFIX")
	if prefix != "" {
		str = strings.Join([]string{prefix, str}, "")
	}
	return str
}

// Create the TLS context, using the key and certificates provided.
func createTLSConfig() (*tls.Config, error) {
	trustedCert, ok := os.LookupEnv("KAFKA_TRUSTED_CERT")
	if !ok {
		return nil, errors.New("Kafka Trusted Certificate not found!")
	}

	clientCertKey, ok := os.LookupEnv("KAFKA_CLIENT_CERT_KEY")
	if !ok {
		return nil, errors.New("Kafka Client Certificate Key not found!")
	}

	clientCert, ok := os.LookupEnv("KAFKA_CLIENT_CERT")
	if !ok {
		return nil, errors.New("Kafka Client Certificate not found!")
	}

	roots := x509.NewCertPool()
	ok = roots.AppendCertsFromPEM([]byte(trustedCert))
	if !ok {
		return nil, errors.New("Unable to parse Root Cert. Please check your Heroku environment.")
	}
	// Setup certs for Sarama
	cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientCertKey))
	if err != nil {
		return nil, err
	}
	return &tls.Config{
		Certificates:       []tls.Certificate{cert},
		InsecureSkipVerify: true,
		RootCAs:            roots,
	}, nil
}

// Extract the host:port pairs from the Kafka URL(s)
func brokerAddresses() ([]string, error) {
	URL, ok := os.LookupEnv("KAFKA_URL")
	if !ok {
		return nil, errors.New("Kafka URL not found!")
	}
	urls := strings.Split(URL, ",")
	addrs := make([]string, len(urls))
	for i, v := range urls {
		u, err := url.Parse(v)
		if err != nil {
			return nil, err
		}
		addrs[i] = u.Host
	}
	return addrs, nil
}
<commit_msg>Adds quick creation for consumers and producers<commit_after>package goheroku

import (
	"crypto/tls"
	"crypto/x509"
	"errors"
	"github.com/Shopify/sarama"
	cluster "github.com/bsm/sarama-cluster"
	"net/url"
	"os"
	"strings"
)

//  NewClusterConsumer creates a bsm sarama-cluster consumer
//  Provide the topic, consumer group and a cluster config
func NewClusterConsumer(topic string, consumerGroup string, cfg *cluster.Config) (*cluster.Consumer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	topic = AppendPrefixTo(topic)
	group := AppendPrefixTo(consumerGroup)
	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}

	consumer, err := cluster.NewConsumer(brokers, group, []string{topic}, cfg)
	if err != nil {
		return nil, err
	}

	return consumer, nil
}

// A quick and easy cluster consumer built with default configs
func NewQuickClusterConsumer(topic string, groupID string) (*cluster.Consumer, error) {
	cfg := cluster.NewConfig()
	consumer, err := NewClusterConsumer(topic, groupID, cfg)
	if err != nil {
		return nil, err
	}
	return consumer, nil
}

// TODO: Investigate use of/need for topic
func NewConsumer(cfg *sarama.Config) (sarama.Consumer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	consumer, err := sarama.NewConsumer(brokers, cfg)
	if err != nil {
		return nil, err
	}

	return consumer, nil
}

// A quick and easy sarama consumer built with default configs
func NewQuickConsumer() (sarama.Consumer, error) {
	cfg := sarama.NewConfig()
	consumer, err := NewConsumer(cfg)
	if err != nil {
		return nil, err
	}
	return consumer, nil
}

//  NewAsyncProducer creates a sarama Async Producer
//  Provide a sarama config
//  For more information about the difference between Async and Sync producers
//  see the sarama documentation
func NewAsyncProducer(cfg *sarama.Config) (sarama.AsyncProducer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	producer, err := sarama.NewAsyncProducer(brokers, cfg)
	if err != nil {
		return nil, err
	}

	return producer, nil
}

// A quick and easy async producer built with default configs
func NewQuickAsyncProducer() (sarama.AsyncProducer, error) {
	cfg := sarama.NewConfig()
	producer, err := NewAsyncProducer(cfg)
	if err != nil {
		return nil, err
	}
	return producer, nil
}

//  NewSyncProducer creates a sarama Sync Producer
//  Provide a sarama config
//  For more information about the difference between Async and Sync producers
//  see the sarama documentation
func NewSyncProducer(cfg *sarama.Config) (sarama.SyncProducer, error) {
	tc, err := createTLSConfig()
	if err != nil {
		return nil, err
	}
	cfg.Net.TLS.Config = tc
	cfg.Net.TLS.Enable = true

	brokers, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	producer, err := sarama.NewSyncProducer(brokers, cfg)
	if err != nil {
		return nil, err
	}

	return producer, nil
}

// A quick and easy sync producer built with default configs
func NewQuickSyncProducer() (sarama.SyncProducer, error) {
	cfg := sarama.NewConfig()
	cfg.Producer.Return.Successes = true
	producer, err := NewSyncProducer(cfg)
	if err != nil {
		return nil, err
	}
	return producer, nil
}

// Endangered function. TODO: Investigate usefulness of client
func NewClient(cfg *sarama.Config) (sarama.Client, error) {
	addrs, err := brokerAddresses()
	if err != nil {
		return nil, err
	}
	client, err := sarama.NewClient(addrs, cfg)
	if err != nil {
		return nil, err
	}
	return client, nil
}

// To specify the topic or consumer group, the Kafka prefix needs
// to appended to it. This function makes it possible without having
// access to the app config.
func AppendPrefixTo(str string) string {
	prefix := os.Getenv("KAFKA_PREFIX")
	if prefix != "" {
		str = strings.Join([]string{prefix, str}, "")
	}
	return str
}

// Create the TLS context, using the key and certificates provided.
func createTLSConfig() (*tls.Config, error) {
	trustedCert, ok := os.LookupEnv("KAFKA_TRUSTED_CERT")
	if !ok {
		return nil, errors.New("Kafka Trusted Certificate not found!")
	}

	clientCertKey, ok := os.LookupEnv("KAFKA_CLIENT_CERT_KEY")
	if !ok {
		return nil, errors.New("Kafka Client Certificate Key not found!")
	}

	clientCert, ok := os.LookupEnv("KAFKA_CLIENT_CERT")
	if !ok {
		return nil, errors.New("Kafka Client Certificate not found!")
	}

	roots := x509.NewCertPool()
	ok = roots.AppendCertsFromPEM([]byte(trustedCert))
	if !ok {
		return nil, errors.New("Unable to parse Root Cert. Please check your Heroku environment.")
	}
	// Setup certs for Sarama
	cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientCertKey))
	if err != nil {
		return nil, err
	}
	return &tls.Config{
		Certificates:       []tls.Certificate{cert},
		InsecureSkipVerify: true,
		RootCAs:            roots,
	}, nil
}

// Extract the host:port pairs from the Kafka URL(s)
func brokerAddresses() ([]string, error) {
	URL, ok := os.LookupEnv("KAFKA_URL")
	if !ok {
		return nil, errors.New("Kafka URL not found!")
	}
	urls := strings.Split(URL, ",")
	addrs := make([]string, len(urls))
	for i, v := range urls {
		u, err := url.Parse(v)
		if err != nil {
			return nil, err
		}
		addrs[i] = u.Host
	}
	return addrs, nil
}
<|endoftext|>


<commit_before>package main

import (
	"./utils"
	"fmt"
	"os"

	"github.com/codegangsta/cli"
)

func Contains(s []string, e string) bool {
	for _, _s := range s {
		if _s == e {
			return true
		}
	}
	return false
}

func ReplaceArgsByAlias(src []string) []string {
	var dest []string
	var subcommands []string

	config, err := LoadConfig()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return src
	}

	for _, cmd := range Commands {
		subcommands = append(subcommands, cmd.Name)
	}

	replaceEnd := false
	for _, arg := range src {
		if replaceEnd {
			dest = append(dest, arg)
			continue
		}

		if Contains(subcommands, arg) {
			replaceEnd = true
		}

		alias := config.Alias[arg]
		if len(alias) > 0 {
			result, err := utils.Split(alias, ' ', '"')
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return src
			}
			dest = append(dest, result...)
			replaceEnd = true
		} else {
			dest = append(dest, arg)
		}
	}

	return dest
}

func main() {
	app := cli.NewApp()
	app.Name = "gookmark"
	app.Version = Version
	app.Usage = ""
	app.Author = "nocd5"
	app.Email = "nocd5rd@gmail.com"
	app.Commands = Commands

	app.Run(ReplaceArgsByAlias(os.Args))
}
<commit_msg>Cahnge utils import path<commit_after>package main

import (
  "github.com/nocd5/gookmark/utils"
	"fmt"
	"os"

	"github.com/codegangsta/cli"
)

func Contains(s []string, e string) bool {
	for _, _s := range s {
		if _s == e {
			return true
		}
	}
	return false
}

func ReplaceArgsByAlias(src []string) []string {
	var dest []string
	var subcommands []string

	config, err := LoadConfig()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return src
	}

	for _, cmd := range Commands {
		subcommands = append(subcommands, cmd.Name)
	}

	replaceEnd := false
	for _, arg := range src {
		if replaceEnd {
			dest = append(dest, arg)
			continue
		}

		if Contains(subcommands, arg) {
			replaceEnd = true
		}

		alias := config.Alias[arg]
		if len(alias) > 0 {
			result, err := utils.Split(alias, ' ', '"')
			if err != nil {
				fmt.Fprintln(os.Stderr, err)
				return src
			}
			dest = append(dest, result...)
			replaceEnd = true
		} else {
			dest = append(dest, arg)
		}
	}

	return dest
}

func main() {
	app := cli.NewApp()
	app.Name = "gookmark"
	app.Version = Version
	app.Usage = ""
	app.Author = "nocd5"
	app.Email = "nocd5rd@gmail.com"
	app.Commands = Commands

	app.Run(ReplaceArgsByAlias(os.Args))
}
<|endoftext|>


<commit_before>package gotiming

import (
	"fmt"
	"time"
)

import ()

type Timing struct {
	name      string
	start     time.Time
	end       time.Time
	timings   map[string]time.Time
	durations map[string]time.Duration
}

func NewTiming(name string) *Timing {
	ret := new(Timing)
	ret.name = name
	ret.start = time.Now()
	ret.timings = make(map[string]time.Time)
	ret.durations = make(map[string]time.Duration)
	return ret
}

func (t *Timing) Start(tag string) {
	t.timings[tag+"-start"] = time.Now()
}

func (t *Timing) End(tag string) (ret time.Duration) {
	if s, ok := t.timings[tag+"-start"]; ok {
		n := time.Now()
		ret = n.Sub(s)
		t.timings[tag+"-end"] = n
		t.durations[tag] = ret
	}
	return
}

func (t *Timing) String() (s string) {
	s += t.name + " -> "
	for k, v := range t.durations {
		s += fmt.Sprintf("%s: %s,", k, v)
	}
	if len(s) > len(t.name+" -> ") {
		s = s[:len(s)-1]
	}
	return
}
<commit_msg>Updated to track the order in which things finished for the final log output.<commit_after>package gotiming

import (
	"fmt"
	"time"
)

import ()

type Timing struct {
	name    string
	start   time.Time
	end     time.Time
	timings map[string]time.Time
	order   []string
}

func NewTiming(name string) *Timing {
	ret := new(Timing)
	ret.name = name
	ret.start = time.Now()
	ret.timings = make(map[string]time.Time)
	ret.order = make([]string, 0)
	return ret
}

func (t *Timing) Start(tag string) {
	t.timings[tag+"-start"] = time.Now()
}

func (t *Timing) End(tag string) (ret time.Duration) {
	if s, ok := t.timings[tag+"-start"]; ok {
		n := time.Now()
		ret = n.Sub(s)
		t.timings[tag+"-end"] = n
		t.order = append(t.order, tag)
	}
	return
}

func (t *Timing) String() (s string) {
	s += t.name + " -> "
	for _, tag := range t.order {
		s += fmt.Sprintf("%s: %s,", tag, t.timings[tag+"-end"].Sub(t.timings[tag+"-start"]))
	}
	if len(s) > len(t.name+" -> ") {
		s = s[:len(s)-1]
	}
	return
}
<|endoftext|>


<commit_before>package rogue

import (
	"image/png"
	"os"
	"testing"

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

func BenchmarkGenerateIsland(b *testing.B) {
	for n := 0; n < b.N; n++ {

		generateIsland(666, 220, 140)
	}
}

func prepareIsland() {
	if island == nil {
		seed := int64(123)
		island = generateIsland(seed, 200, 100)

		islandColImgFile, _ := os.Create("island_test.png")
		png.Encode(islandColImgFile, island.ColoredHeightMapAsImage())
	}

	// clear spawns between tests
	island.Spawns = nil
}

func TestFindFood(t *testing.T) {

	prepareIsland()

	island.addNpcFromType("dwarf")

	// make sure npcs was created
	assert.Equal(t, true, len(island.Spawns) == 1)

	dw := island.Spawns[0]

	// make npc 1 hungry
	dw.Hunger = dw.hungerCap() + 1

	island.Tick()

	// make sure that npc has aged
	assert.Equal(t, true, dw.Age > 0)

	// make sure npc 1 planned action: find food
	assert.Equal(t, "find-food", dw.CurrentAction)
	assert.Equal(t, false, dw.hasItemTypeInInventory("food"))

	// progress until npc found food
	for i := 0; i < 5; i++ { // XXXX need to find action "find food".duration, from actions.yml
		island.Tick()
	}

	// make sure food was found for npc 1
	assert.Equal(t, true, len(dw.Inventory) > 0)
	assert.Equal(t, true, dw.hasItemTypeInInventory("food"))
	assert.Equal(t, false, dw.hasItemTypeInInventory("drink"))

	oldHunger := dw.Hunger
	island.Tick()

	// make sure npc 1 consumed food
	assert.Equal(t, false, dw.hasItemTypeInInventory("food"))

	// make sure hunger went down
	assert.Equal(t, true, dw.Hunger < oldHunger)

	assert.Equal(t, false, dw.isHungry())
}

func TestFindWater(t *testing.T) {

	prepareIsland()

	island.addNpcFromType("dwarf")

	// make sure npcs was created
	assert.Equal(t, true, len(island.Spawns) == 1)

	dw := island.Spawns[0]

	// make npc 2 thirsty
	dw.Thirst = dw.thirstCap() + 1

	island.Tick()

	// make sure npc 2 planned action: find water
	assert.Equal(t, "find-water", dw.CurrentAction)
	assert.Equal(t, false, dw.hasItemTypeInInventory("drink"))

	// progress until npc found food
	for i := 0; i < 5; i++ { // XXXX need to find action "find food".duration, from actions.yml
		island.Tick()
	}

	// make sure water was found for npc 2
	assert.Equal(t, true, len(dw.Inventory) > 0)
	assert.Equal(t, true, dw.hasItemTypeInInventory("drink"))

	oldThirst := dw.Thirst
	island.Tick()
	// make sure npc 2 consumed water
	assert.Equal(t, false, dw.hasItemTypeInInventory("drink"))

	// make sure hunger went down
	assert.Equal(t, true, dw.Thirst < oldThirst)

	assert.Equal(t, false, dw.isThirsty())
}
<commit_msg>:lipstick:<commit_after>package rogue

import (
	"image/png"
	"os"
	"testing"

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

func BenchmarkGenerateIsland(b *testing.B) {
	for n := 0; n < b.N; n++ {

		generateIsland(666, 220, 140)
	}
}

func prepareIsland() {
	if island == nil {
		seed := int64(123)
		island = generateIsland(seed, 200, 100)

		islandColImgFile, _ := os.Create("island_test.png")
		png.Encode(islandColImgFile, island.ColoredHeightMapAsImage())
	}

	// clear spawns between tests
	island.Spawns = nil
}

func TestFindFood(t *testing.T) {

	prepareIsland()

	island.addNpcFromType("dwarf")
	assert.Equal(t, true, len(island.Spawns) == 1)
	dw := island.Spawns[0]

	// make npc hungry
	dw.Hunger = dw.hungerCap() + 1
	island.Tick()

	// make sure that npc has aged
	assert.Equal(t, true, dw.Age > 0)

	// make sure planned action: find food
	assert.Equal(t, "find-food", dw.CurrentAction)
	assert.Equal(t, false, dw.hasItemTypeInInventory("food"))

	// progress until npc found food
	for i := 0; i < 5; i++ { // XXXX need to find action "find food".duration, from actions.yml
		island.Tick()
	}

	// make sure food was found
	assert.Equal(t, true, len(dw.Inventory) > 0)
	assert.Equal(t, true, dw.hasItemTypeInInventory("food"))
	assert.Equal(t, false, dw.hasItemTypeInInventory("drink"))

	oldHunger := dw.Hunger
	island.Tick()

	// make sure npc consumed food
	assert.Equal(t, false, dw.hasItemTypeInInventory("food"))

	// make sure hunger went down
	assert.Equal(t, true, dw.Hunger < oldHunger)
	assert.Equal(t, false, dw.isHungry())
}

func TestFindWater(t *testing.T) {

	prepareIsland()

	island.addNpcFromType("dwarf")
	assert.Equal(t, true, len(island.Spawns) == 1)
	dw := island.Spawns[0]

	// make npc thirsty
	dw.Thirst = dw.thirstCap() + 1
	island.Tick()

	// make sure npc planned action: find water
	assert.Equal(t, "find-water", dw.CurrentAction)
	assert.Equal(t, false, dw.hasItemTypeInInventory("drink"))

	// progress until npc found food
	for i := 0; i < 5; i++ { // XXXX need to find action "find food".duration, from actions.yml
		island.Tick()
	}

	// make sure water was found
	assert.Equal(t, true, len(dw.Inventory) > 0)
	assert.Equal(t, true, dw.hasItemTypeInInventory("drink"))

	oldThirst := dw.Thirst
	island.Tick()

	// make sure npc consumed water
	assert.Equal(t, false, dw.hasItemTypeInInventory("drink"))

	// make sure thirst went down
	assert.Equal(t, true, dw.Thirst < oldThirst)
	assert.Equal(t, false, dw.isThirsty())
}
<|endoftext|>


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

package chip8

import "github.com/nsf/termbox-go"

const (
	GraphicsWidth  = 64 // Pixels
	GraphicsHeight = 32 // Pixels
)

// Display represents the output display for the CHIP-8 graphics array.
type Display interface {
	// Render should render the current graphics array to the display.
	Render(*Graphics) error
}

type DisplayFunc func(*Graphics) error

func (f DisplayFunc) Render(g *Graphics) error {
	return f(g)
}

// NullDisplay is an implementation of the Display interface that does nothing.
var NullDisplay = DisplayFunc(func(*Graphics) error {
	return nil
})

// Graphics represents the graphics array for the CHIP-8.
type Graphics struct {
	// The raw pixels of the graphics array.
	Pixels [GraphicsWidth * GraphicsHeight]byte

	// The display to render to. The nil value is the DefaultDisplay.
	Display
}

// DrawSprite draws a sprite to the graphics array starting at coording x, y.
// If there is a collision, WriteSprite returns true.
func (g *Graphics) WriteSprite(sprite []byte, x, y byte) (collision bool) {
	n := len(sprite)

	for yl := 0; yl < n; yl++ {
		// A row of sprite data.
		r := sprite[yl]

		for xl := 0; xl < 8; xl++ {
			// This represents a mask for the bit that we
			// care about for this coordinate.
			i := 0x80 >> byte(xl)

			// Whether the bit is set or not.
			on := (r & byte(i)) == byte(i)

			// The X position for this pixel
			xp := uint16(x) + uint16(xl)
			if xp >= GraphicsWidth {
				xp = xp - GraphicsWidth
			}

			// The Y position for this pixel
			yp := uint16(y) + uint16(yl)
			if yp >= GraphicsHeight {
				yp = yp - GraphicsHeight
			}

			if g.Set(xp, yp, on) {
				collision = true
			}
		}
	}

	return
}

// Clear clears the display.
func (g *Graphics) Clear() {
	g.EachPixel(func(_, _ uint16, addr int) {
		g.Pixels[addr] = 0
	})
}

// Draw draws the graphics array to the Display.
func (g *Graphics) Draw() error {
	return g.display().Render(g)
}

// EachPixel yields each pixel in the graphics array to fn.
func (g *Graphics) EachPixel(fn func(x, y uint16, addr int)) {
	for y := 0; y < GraphicsHeight-1; y++ {
		for x := 0; x < GraphicsWidth-1; x++ {
			a := y*GraphicsWidth + x
			fn(uint16(x), uint16(y), a)
		}
	}
}

// Set turns the pixel at the given coordinates on or off. If there's a
// collision, it returns true.
func (g *Graphics) Set(x, y uint16, on bool) (collision bool) {
	a := x + y*GraphicsWidth

	if g.Pixels[a] == 0x01 {
		collision = true
	}

	var v byte
	if on {
		v = 0x01
	}

	g.Pixels[a] = g.Pixels[a] ^ v

	return
}

func (g *Graphics) display() Display {
	if g.Display == nil {
		return DefaultDisplay
	}

	return g.Display
}

var (
	fg = termbox.ColorBlack
	bg = termbox.ColorDefault
)

// termboxInit initializes termbox with appropriate settings. This should be
// called before using the TermboxDisplay and TermboxKeypad.
func termboxInit(bg termbox.Attribute) error {
	if err := termbox.Init(); err != nil {
		return err
	}

	termbox.HideCursor()

	if err := termbox.Clear(bg, bg); err != nil {
		return err
	}

	return termbox.Flush()
}

// TermboxDisplay is an implementation of the Display interface that renders
// the graphics array to the terminal.
type TermboxDisplay struct {
	fg, bg termbox.Attribute
}

// NewTermboxDisplay returns a new TermboxDisplay instance.
func NewTermboxDisplay(fg, bg termbox.Attribute) (*TermboxDisplay, error) {
	return &TermboxDisplay{
		fg: fg,
		bg: bg,
	}, termboxInit(bg)
}

// Render renders the graphics array to the terminal using Termbox.
func (d *TermboxDisplay) Render(g *Graphics) error {
	g.EachPixel(func(x, y uint16, addr int) {
		v := ' '

		if g.Pixels[addr] == 0x01 {
			v = '*'
		}

		termbox.SetCell(
			int(x),
			int(y),
			v,
			d.fg,
			d.bg,
		)
	})

	return termbox.Flush()
}

func (d *TermboxDisplay) Close() {
	termbox.Close()
}
<commit_msg>Cleanup.<commit_after>// Copyright 2014 Eric Holmes.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package chip8

import "github.com/nsf/termbox-go"

const (
	GraphicsWidth  = 64 // Pixels
	GraphicsHeight = 32 // Pixels
)

// Display represents the output display for the CHIP-8 graphics array.
type Display interface {
	// Render should render the current graphics array to the display.
	Render(*Graphics) error
}

type DisplayFunc func(*Graphics) error

func (f DisplayFunc) Render(g *Graphics) error {
	return f(g)
}

// NullDisplay is an implementation of the Display interface that does nothing.
var NullDisplay = DisplayFunc(func(*Graphics) error {
	return nil
})

// Graphics represents the graphics array for the CHIP-8.
type Graphics struct {
	// The raw pixels of the graphics array.
	Pixels [GraphicsWidth * GraphicsHeight]byte

	// The display to render to. The nil value is the DefaultDisplay.
	Display
}

// DrawSprite draws a sprite to the graphics array starting at coording x, y.
// If there is a collision, WriteSprite returns true.
func (g *Graphics) WriteSprite(sprite []byte, x, y byte) (collision bool) {
	n := len(sprite)

	for yl := 0; yl < n; yl++ {
		// A row of sprite data.
		r := sprite[yl]

		for xl := 0; xl < 8; xl++ {
			// This represents a mask for the bit that we
			// care about for this coordinate.
			i := 0x80 >> byte(xl)

			// Whether the bit is set or not.
			on := (r & byte(i)) == byte(i)

			// The X position for this pixel
			xp := uint16(x) + uint16(xl)
			if xp >= GraphicsWidth {
				xp = xp - GraphicsWidth
			}

			// The Y position for this pixel
			yp := uint16(y) + uint16(yl)
			if yp >= GraphicsHeight {
				yp = yp - GraphicsHeight
			}

			if g.Set(xp, yp, on) {
				collision = true
			}
		}
	}

	return
}

// Clear clears the display.
func (g *Graphics) Clear() {
	g.EachPixel(func(_, _ uint16, addr int) {
		g.Pixels[addr] = 0
	})
}

// Draw draws the graphics array to the Display.
func (g *Graphics) Draw() error {
	return g.display().Render(g)
}

// EachPixel yields each pixel in the graphics array to fn.
func (g *Graphics) EachPixel(fn func(x, y uint16, addr int)) {
	for y := 0; y < GraphicsHeight-1; y++ {
		for x := 0; x < GraphicsWidth-1; x++ {
			a := y*GraphicsWidth + x
			fn(uint16(x), uint16(y), a)
		}
	}
}

// Set turns the pixel at the given coordinates on or off. If there's a
// collision, it returns true.
func (g *Graphics) Set(x, y uint16, on bool) (collision bool) {
	a := x + y*GraphicsWidth

	if g.Pixels[a] == 0x01 {
		collision = true
	}

	var v byte
	if on {
		v = 0x01
	}

	g.Pixels[a] = g.Pixels[a] ^ v

	return
}

func (g *Graphics) display() Display {
	if g.Display == nil {
		return DefaultDisplay
	}

	return g.Display
}

// termboxInit initializes termbox with appropriate settings. This should be
// called before using the TermboxDisplay and TermboxKeypad.
func termboxInit(bg termbox.Attribute) error {
	if err := termbox.Init(); err != nil {
		return err
	}

	termbox.HideCursor()

	if err := termbox.Clear(bg, bg); err != nil {
		return err
	}

	return termbox.Flush()
}

// TermboxDisplay is an implementation of the Display interface that renders
// the graphics array to the terminal.
type TermboxDisplay struct {
	fg, bg termbox.Attribute
}

// NewTermboxDisplay returns a new TermboxDisplay instance.
func NewTermboxDisplay(fg, bg termbox.Attribute) (*TermboxDisplay, error) {
	return &TermboxDisplay{
		fg: fg,
		bg: bg,
	}, termboxInit(bg)
}

// Render renders the graphics array to the terminal using Termbox.
func (d *TermboxDisplay) Render(g *Graphics) error {
	g.EachPixel(func(x, y uint16, addr int) {
		v := ' '

		if g.Pixels[addr] == 0x01 {
			v = '*'
		}

		termbox.SetCell(
			int(x),
			int(y),
			v,
			d.fg,
			d.bg,
		)
	})

	return termbox.Flush()
}

func (d *TermboxDisplay) Close() {
	termbox.Close()
}
<|endoftext|>


<commit_before>/*
  Go Language Raspberry Pi Interface
  (c) Copyright David Thorpe 2016-2020
  All Rights Reserved
  For Licensing and Usage information, please see LICENSE.md
*/

package gopi

import (
	"fmt"
	"strings"
)

////////////////////////////////////////////////////////////////////////////////
// TYPES

type (
	// SurfaceFlags are flags associated with surface
	SurfaceFlags uint16

	// SurfaceCallback
	SurfaceCallback func(SurfaceManager) error
)

// Color including opacity
type Color struct {
	R, G, B, A float32
}

////////////////////////////////////////////////////////////////////////////////
// INTERFACES

// SurfaceManager allows you to open, close and move surfaces around an open display
type SurfaceManager interface {
	// Return the display associated with the surface manager
	Display() Display

	// Return the name of the surface manager
	Name() string

	// Return capabilities for the GPU
	Types() []SurfaceFlags

	// Do CreateSurface, updates and drawing within this method
	Do(SurfaceCallback) error

	// Create and destroy surfaces
	CreateSurfaceWithBitmap(Bitmap, SurfaceFlags, float32, uint16, Point, Size) (Surface, error)
	CreateSurface(SurfaceFlags, float32, uint16, Point, Size) (Surface, error)
	CreateBackground(SurfaceFlags, float32) (Surface, error)
	DestroySurface(Surface) error

	// Create and destroy bitmaps
	CreateBitmap(SurfaceFlags, Size) (Bitmap, error)
	CreateSnapshot(SurfaceFlags) (Bitmap, error)
	DestroyBitmap(Bitmap) error

	// Implements gopi.Unit
	Unit
}

// Surface defines an on-screen rectanglar surface
type Surface interface {
	Type() SurfaceFlags
	Size() Size
	Origin() Point
	Opacity() float32
	Layer() uint16
	Bitmap() Bitmap
}

// Bitmap defines a rectangular bitmap which can be used by the GPU
type Bitmap interface {
	Type() SurfaceFlags
	Size() Size

	// Clear bitmap
	ClearToColor(Color)

	// Paint a single pixel
	PaintPixel(Color,Point)

}

////////////////////////////////////////////////////////////////////////////////
// CONSTANTS

const (
	// SurfaceFlags - surface binding
	SURFACE_FLAG_NONE       SurfaceFlags = 0x0000
	SURFACE_FLAG_BITMAP     SurfaceFlags = 0x0001 // Bitmap
	SURFACE_FLAG_OPENGL     SurfaceFlags = 0x0002
	SURFACE_FLAG_OPENGL_ES  SurfaceFlags = 0x0003
	SURFACE_FLAG_OPENGL_ES2 SurfaceFlags = 0x0004
	SURFACE_FLAG_OPENVG     SurfaceFlags = 0x0005 // 2D Vector
	SURFACE_FLAG_TYPEMASK   SurfaceFlags = 0x000F
	// SurfaceFlags - surface configuration
	SURFACE_FLAG_RGBA32     SurfaceFlags = 0x0000 // 4 bytes per pixel
	SURFACE_FLAG_RGB888     SurfaceFlags = 0x0010 // 3 bytes per pixel
	SURFACE_FLAG_RGB565     SurfaceFlags = 0x0020 // 2 bytes per pixel
	SURFACE_FLAG_CONFIGMASK SurfaceFlags = 0x00F0
	// SurfaceFlags - modifiers
	SURFACE_FLAG_ALPHA_FROM_SOURCE SurfaceFlags = 0x0100
	SURFACE_FLAG_MODMASK           SurfaceFlags = 0x0F00
)

const (
	// SurfaceLayer
	SURFACE_LAYER_BACKGROUND uint16 = 0x0000
	SURFACE_LAYER_DEFAULT    uint16 = 0x0001
	SURFACE_LAYER_MAX        uint16 = 0xFFFE
	SURFACE_LAYER_CURSOR     uint16 = 0xFFFF
)

////////////////////////////////////////////////////////////////////////////////
// GLOBALS

// Standard Colors
var (
	ColorRed         = Color{1.0, 0.0, 0.0, 1.0}
	ColorGreen       = Color{0.0, 1.0, 0.0, 1.0}
	ColorBlue        = Color{0.0, 0.0, 1.0, 1.0}
	ColorWhite       = Color{1.0, 1.0, 1.0, 1.0}
	ColorBlack       = Color{0.0, 0.0, 0.0, 1.0}
	ColorPurple      = Color{1.0, 0.0, 1.0, 1.0}
	ColorCyan        = Color{0.0, 1.0, 1.0, 1.0}
	ColorYellow      = Color{1.0, 1.0, 0.0, 1.0}
	ColorDarkGrey    = Color{0.25, 0.25, 0.25, 1.0}
	ColorLightGrey   = Color{0.75, 0.75, 0.75, 1.0}
	ColorMidGrey     = Color{0.5, 0.5, 0.5, 1.0}
	ColorTransparent = Color{}
)

////////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATIONS

// RGBA returns uint32 values for color
func (c Color) RGBA() (r, g, b, a uint32) {
	return uint32(c.R*float32(0xFFFF)) & uint32(0xFFFF), uint32(c.G*float32(0xFFFF)) & uint32(0xFFFF), uint32(c.B*float32(0xFFFF)) & uint32(0xFFFF), uint32(c.A*float32(0xFFFF)) & uint32(0xFFFF)
}

// Type returns the type of the surface
func (f SurfaceFlags) Type() SurfaceFlags {
	return f & SURFACE_FLAG_TYPEMASK
}

// Config returns the configuration of the surface
func (f SurfaceFlags) Config() SurfaceFlags {
	return f & SURFACE_FLAG_CONFIGMASK
}

// Mod returns surface modifiers
func (f SurfaceFlags) Mod() SurfaceFlags {
	return f & SURFACE_FLAG_MODMASK
}

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (f SurfaceFlags) TypeString() string {
	switch f.Type() {
	case SURFACE_FLAG_BITMAP:
		return "SURFACE_FLAG_BITMAP"
	case SURFACE_FLAG_OPENGL:
		return "SURFACE_FLAG_OPENGL"
	case SURFACE_FLAG_OPENGL_ES:
		return "SURFACE_FLAG_OPENGL_ES"
	case SURFACE_FLAG_OPENGL_ES2:
		return "SURFACE_FLAG_OPENGL_ES2"
	case SURFACE_FLAG_OPENVG:
		return "SURFACE_FLAG_OPENVG"
	default:
		return "[?? Invalid SurfaceFlags value]"
	}
}

func (f SurfaceFlags) ConfigString() string {
	switch f.Config() {
	case SURFACE_FLAG_RGBA32:
		return "SURFACE_FLAG_RGBA32"
	case SURFACE_FLAG_RGB888:
		return "SURFACE_FLAG_RGB888"
	case SURFACE_FLAG_RGB565:
		return "SURFACE_FLAG_RGB565"
	default:
		return "[?? Invalid SurfaceFlags value]"
	}
}

func (f SurfaceFlags) ModString() string {
	m := f.Mod()
	switch {
	case m == 0:
		return ""
	case m&SURFACE_FLAG_ALPHA_FROM_SOURCE == SURFACE_FLAG_ALPHA_FROM_SOURCE:
		return "SURFACE_FLAG_ALPHA_FROM_SOURCE"
	default:
		return "[?? Invalid SurfaceFlags value]"
	}
}

func (f SurfaceFlags) String() string {
	parts := ""
	parts += "|" + f.TypeString()
	parts += "|" + f.ConfigString()
	parts += "|" + f.ModString()
	return strings.Trim(parts, "|")
}

func (c Color) String() string {
	return fmt.Sprintf("Color{ %.1f,%.1f,%.1f,%.1f }", c.R, c.G, c.B, c.A)
}
<commit_msg>changed strigify<commit_after>/*
  Go Language Raspberry Pi Interface
  (c) Copyright David Thorpe 2016-2020
  All Rights Reserved
  For Licensing and Usage information, please see LICENSE.md
*/

package gopi

import (
	"fmt"
	"strings"
)

////////////////////////////////////////////////////////////////////////////////
// TYPES

type (
	// SurfaceFlags are flags associated with surface
	SurfaceFlags uint16

	// SurfaceCallback
	SurfaceCallback func(SurfaceManager) error
)

// Color including opacity
type Color struct {
	R, G, B, A float32
}

////////////////////////////////////////////////////////////////////////////////
// INTERFACES

// SurfaceManager allows you to open, close and move surfaces around an open display
type SurfaceManager interface {
	// Return the display associated with the surface manager
	Display() Display

	// Return the name of the surface manager
	Name() string

	// Return capabilities for the GPU
	Types() []SurfaceFlags

	// Do CreateSurface, updates and drawing within this method
	Do(SurfaceCallback) error

	// Create and destroy surfaces
	CreateSurfaceWithBitmap(Bitmap, SurfaceFlags, float32, uint16, Point, Size) (Surface, error)
	CreateSurface(SurfaceFlags, float32, uint16, Point, Size) (Surface, error)
	CreateBackground(SurfaceFlags, float32) (Surface, error)
	DestroySurface(Surface) error

	// Create and destroy bitmaps
	CreateBitmap(SurfaceFlags, Size) (Bitmap, error)
	CreateSnapshot(SurfaceFlags) (Bitmap, error)
	DestroyBitmap(Bitmap) error

	// Implements gopi.Unit
	Unit
}

// Surface defines an on-screen rectanglar surface
type Surface interface {
	Type() SurfaceFlags
	Size() Size
	Origin() Point
	Opacity() float32
	Layer() uint16
	Bitmap() Bitmap
}

// Bitmap defines a rectangular bitmap which can be used by the GPU
type Bitmap interface {
	Type() SurfaceFlags
	Size() Size

	// Clear bitmap
	ClearToColor(Color)

	// Paint a single pixel
	PaintPixel(Color, Point)
}

////////////////////////////////////////////////////////////////////////////////
// CONSTANTS

const (
	// SurfaceFlags - surface binding
	SURFACE_FLAG_NONE       SurfaceFlags = 0x0000
	SURFACE_FLAG_BITMAP     SurfaceFlags = 0x0001 // Bitmap
	SURFACE_FLAG_OPENGL     SurfaceFlags = 0x0002
	SURFACE_FLAG_OPENGL_ES  SurfaceFlags = 0x0003
	SURFACE_FLAG_OPENGL_ES2 SurfaceFlags = 0x0004
	SURFACE_FLAG_OPENVG     SurfaceFlags = 0x0005 // 2D Vector
	SURFACE_FLAG_TYPEMASK   SurfaceFlags = 0x000F
	// SurfaceFlags - surface configuration
	SURFACE_FLAG_RGBA32     SurfaceFlags = 0x0000 // 4 bytes per pixel
	SURFACE_FLAG_RGB888     SurfaceFlags = 0x0010 // 3 bytes per pixel
	SURFACE_FLAG_RGB565     SurfaceFlags = 0x0020 // 2 bytes per pixel
	SURFACE_FLAG_CONFIGMASK SurfaceFlags = 0x00F0
	// SurfaceFlags - modifiers
	SURFACE_FLAG_ALPHA_FROM_SOURCE SurfaceFlags = 0x0100
	SURFACE_FLAG_MODMASK           SurfaceFlags = 0x0F00
)

const (
	// SurfaceLayer
	SURFACE_LAYER_BACKGROUND uint16 = 0x0000
	SURFACE_LAYER_DEFAULT    uint16 = 0x0001
	SURFACE_LAYER_MAX        uint16 = 0xFFFE
	SURFACE_LAYER_CURSOR     uint16 = 0xFFFF
)

////////////////////////////////////////////////////////////////////////////////
// GLOBALS

// Standard Colors
var (
	ColorRed         = Color{1.0, 0.0, 0.0, 1.0}
	ColorGreen       = Color{0.0, 1.0, 0.0, 1.0}
	ColorBlue        = Color{0.0, 0.0, 1.0, 1.0}
	ColorWhite       = Color{1.0, 1.0, 1.0, 1.0}
	ColorBlack       = Color{0.0, 0.0, 0.0, 1.0}
	ColorPurple      = Color{1.0, 0.0, 1.0, 1.0}
	ColorCyan        = Color{0.0, 1.0, 1.0, 1.0}
	ColorYellow      = Color{1.0, 1.0, 0.0, 1.0}
	ColorDarkGrey    = Color{0.25, 0.25, 0.25, 1.0}
	ColorLightGrey   = Color{0.75, 0.75, 0.75, 1.0}
	ColorMidGrey     = Color{0.5, 0.5, 0.5, 1.0}
	ColorTransparent = Color{}
)

////////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATIONS

// RGBA returns uint32 values for color
func (c Color) RGBA() (r, g, b, a uint32) {
	return uint32(c.R*float32(0xFFFF)) & uint32(0xFFFF), uint32(c.G*float32(0xFFFF)) & uint32(0xFFFF), uint32(c.B*float32(0xFFFF)) & uint32(0xFFFF), uint32(c.A*float32(0xFFFF)) & uint32(0xFFFF)
}

// Type returns the type of the surface
func (f SurfaceFlags) Type() SurfaceFlags {
	return f & SURFACE_FLAG_TYPEMASK
}

// Config returns the configuration of the surface
func (f SurfaceFlags) Config() SurfaceFlags {
	return f & SURFACE_FLAG_CONFIGMASK
}

// Mod returns surface modifiers
func (f SurfaceFlags) Mod() SurfaceFlags {
	return f & SURFACE_FLAG_MODMASK
}

////////////////////////////////////////////////////////////////////////////////
// STRINGIFY

func (f SurfaceFlags) TypeString() string {
	switch f.Type() {
	case SURFACE_FLAG_BITMAP:
		return "SURFACE_FLAG_BITMAP"
	case SURFACE_FLAG_OPENGL:
		return "SURFACE_FLAG_OPENGL"
	case SURFACE_FLAG_OPENGL_ES:
		return "SURFACE_FLAG_OPENGL_ES"
	case SURFACE_FLAG_OPENGL_ES2:
		return "SURFACE_FLAG_OPENGL_ES2"
	case SURFACE_FLAG_OPENVG:
		return "SURFACE_FLAG_OPENVG"
	default:
		return "[?? Invalid SurfaceFlags value]"
	}
}

func (f SurfaceFlags) ConfigString() string {
	switch f.Config() {
	case SURFACE_FLAG_RGBA32:
		return "SURFACE_FLAG_RGBA32"
	case SURFACE_FLAG_RGB888:
		return "SURFACE_FLAG_RGB888"
	case SURFACE_FLAG_RGB565:
		return "SURFACE_FLAG_RGB565"
	default:
		return "[?? Invalid SurfaceFlags value]"
	}
}

func (f SurfaceFlags) ModString() string {
	m := f.Mod()
	switch {
	case m == 0:
		return ""
	case m&SURFACE_FLAG_ALPHA_FROM_SOURCE == SURFACE_FLAG_ALPHA_FROM_SOURCE:
		return "SURFACE_FLAG_ALPHA_FROM_SOURCE"
	default:
		return "[?? Invalid SurfaceFlags value]"
	}
}

func (f SurfaceFlags) String() string {
	parts := ""
	if f.Type() != SURFACE_FLAG_NONE {
		parts += "|" + f.TypeString()
	}
	parts += "|" + f.ConfigString()
	if f.Mod() != SURFACE_FLAG_NONE {
		parts += "|" + f.ModString()
	}
	return strings.Trim(parts, "|")
}

func (c Color) String() string {
	return fmt.Sprintf("Color{ %.1f,%.1f,%.1f,%.1f }", c.R, c.G, c.B, c.A)
}
<|endoftext|>


<commit_before>package rtf

import (
  "io/ioutil"
  "unicode"
  "regexp"

  "golang.org/x/text/runes"
  "golang.org/x/text/transform"
  "golang.org/x/text/encoding/charmap"
)

func WriteAsWindows1252 (src string, dst string) error {
  bSrc, err := ioutil.ReadFile(src)
  if err != nil {
      return err
  }

  bDst := make([]byte, len(bSrc)*2)
  replaceNonAscii := runes.Map(func(r rune) rune {
		if r > unicode.MaxASCII {
			return rune('?')
		}
		return r
  })
  transformer := transform.Chain(replaceNonAscii, charmap.Windows1252.NewEncoder())
  _, _, err = transformer.Transform(bDst, bSrc, true)
  if err != nil {
      return err
  }

  // not sure how to catch for [^\r]\n...
  re := regexp.MustCompile(`\n`)
  dS := re.ReplaceAllString(string(bDst), "\r\n")

  return ioutil.WriteFile(dst, []byte(dS), 0644)
}

func WriteAsRtf (src string, dst string, reencode bool) error {

  bSrc, err := ioutil.ReadFile(src)
  if err != nil {
      return err
  }

  var bDst []byte

  if reencode {
    bDst = make([]byte, len(bSrc))
    replaceNonAscii := runes.Map(func(r rune) rune {
  		if r > unicode.MaxASCII {
  			return rune('?')
  		}
  		return r
    })
    transformer := transform.Chain(replaceNonAscii, charmap.Windows1252.NewEncoder())
    _, _, err := transformer.Transform(bDst, bSrc, true)
    if err != nil {
        return err
    }

    re := regexp.MustCompile(`\n`)
    dS := re.ReplaceAllString(string(bDst), "\r\n")

    bDst = []byte(dS)

  } else {
    bDst = bSrc
  }

  re := regexp.MustCompile(`(\r\n|\n)`)
  sDat := re.ReplaceAllString(string(bDst), "\n\\line ")
  sDat = "{\\rtf1\\ansi\n"+sDat+"\n}"

  return ioutil.WriteFile(dst, []byte(sDat), 0644)
}

func IsRtf(src string) bool {
  dat, err := ioutil.ReadFile(src)
  if err != nil {
      return false
  }
  sDat := string(dat)
  if len(sDat)>4 {
    return sDat[0:5]=="{\\rtf"
  }
  return false
}
<commit_msg>Improve EOL replacement<commit_after>package rtf

import (
  "io/ioutil"
  "unicode"
  "strings"

  "golang.org/x/text/runes"
  "golang.org/x/text/transform"
  "golang.org/x/text/encoding/charmap"
)

func WriteAsWindows1252 (src string, dst string) error {
  bSrc, err := ioutil.ReadFile(src)
  if err != nil {
      return err
  }

  bDst := make([]byte, len(bSrc)*2)
  replaceNonAscii := runes.Map(func(r rune) rune {
		if r > unicode.MaxASCII {
			return rune('?')
		}
		return r
  })
  transformer := transform.Chain(replaceNonAscii, charmap.Windows1252.NewEncoder())
  _, _, err = transformer.Transform(bDst, bSrc, true)
  if err != nil {
      return err
  }

  dS := strings.NewReplacer("\r\n", "\r\n", "\n", "\r\n").Replace(string(bDst))
  return ioutil.WriteFile(dst, []byte(dS), 0644)
}

func WriteAsRtf (src string, dst string, reencode bool) error {

  bSrc, err := ioutil.ReadFile(src)
  if err != nil {
      return err
  }

  var bDst []byte

  if reencode {
    bDst = make([]byte, len(bSrc))
    replaceNonAscii := runes.Map(func(r rune) rune {
  		if r > unicode.MaxASCII {
  			return rune('?')
  		}
  		return r
    })
    transformer := transform.Chain(replaceNonAscii, charmap.Windows1252.NewEncoder())
    _, _, err := transformer.Transform(bDst, bSrc, true)
    if err != nil {
        return err
    }

    dS := strings.NewReplacer("\r\n", "\r\n", "\n", "\r\n").Replace(string(bDst))

    bDst = []byte(dS)

  } else {
    bDst = bSrc
  }

  sDat := strings.NewReplacer("\n", "\n\\line ").Replace(string(bDst))
  sDat = "{\\rtf1\\ansi\r\n"+sDat+"\r\n}"

  return ioutil.WriteFile(dst, []byte(sDat), 0644)
}

func IsRtf(src string) bool {
  dat, err := ioutil.ReadFile(src)
  if err != nil {
      return false
  }
  sDat := string(dat)
  if len(sDat)>4 {
    return sDat[0:5]=="{\\rtf"
  }
  return false
}
<|endoftext|>


<commit_before>package infrastructure

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	httpurl "net/url"
	"time"
)

type Client struct {
	url    *httpurl.URL
	Client *http.Client
}

func New(url string) (*Client, error) {
	u, err := httpurl.Parse(url)
	if err != nil {
		return nil, err
	}
	return &Client{
		url:    u,
		Client: &http.Client{},
	}, nil
}

func NewFromURL(url httpurl.URL) *Client {
	return &Client{&url, &http.Client{}}
}

type TimeInterval struct {
	From time.Time
	To   time.Time
}

func (t *TimeInterval) Check() error {
	if t.From.After(t.To) {
		return errors.New("From must be before To.")
	}
	return nil
}

func graphiteDateFormat(t time.Time) string {
	return fmt.Sprintf("%02d:%02d_%d%02d%02d", t.Hour(), t.Minute(), t.Year(), t.Month(), t.Day())
}

type FloatDatapoint struct {
	Time  time.Time
	Value *float64
}

type IntDatapoint struct {
	Time  time.Time
	Value *int64
}

type Datapoints struct {
	// Previous error to make API nicer.
	err    error
	points [][]interface{}
}

func (d Datapoints) AsInts() ([]IntDatapoint, error) {
	if d.err != nil {
		return nil, d.err
	}

	points := make([]IntDatapoint, 0, len(d.points))
	for _, point := range d.points {
		jsonUnixTime, ok := point[1].(json.Number)
		if !ok {
			return nil, errors.New("Unix timestamp not number.")
		}
		unixTime, err := jsonUnixTime.Int64()
		if err != nil {
			return nil, errors.New("Unix time not proper number.")
		}

		var value *int64
		if point[0] != nil {
			jsonValue, ok := point[0].(json.Number)
			if !ok {
				return nil, errors.New("Value not a number.")
			}
			value = new(int64)
			*value, err = jsonValue.Int64()
			if err != nil {
				return nil, errors.New("Value not proper number.")
			}
		}
		points = append(points, IntDatapoint{time.Unix(unixTime, 0), value})
	}

	return points, nil
}

func (d Datapoints) AsFloats() ([]FloatDatapoint, error) {
	if d.err != nil {
		return nil, d.err
	}

	points := make([]FloatDatapoint, 0, len(d.points))
	for _, point := range d.points {
		jsonUnixTime, ok := point[1].(json.Number)
		if !ok {
			return nil, errors.New("Unix timestamp not number.")
		}
		unixTime, err := jsonUnixTime.Int64()
		if err != nil {
			return nil, errors.New("Unix time not proper number.")
		}

		var value *float64
		if point[0] != nil {
			jsonValue, ok := point[0].(json.Number)
			if !ok {
				return nil, errors.New("Value not a number.")
			}
			value = new(float64)
			*value, err = jsonValue.Float64()
			if err != nil {
				return nil, errors.New("Value not proper number.")
			}
		}
		points = append(points, FloatDatapoint{time.Unix(unixTime, 0), value})
	}

	return points, nil
}

func constructQueryPart(q string) httpurl.Values {
	query := make(httpurl.Values)
	query.Add("target", q)
	query.Add("format", "json")
	return query
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryInts(q string, interval TimeInterval) ([]IntDatapoint, error) {
	return g.Query(q, interval).AsInts()
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryFloats(q string, interval TimeInterval) ([]FloatDatapoint, error) {
	return g.Query(q, interval).AsFloats()
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryIntsSince(q string, ago time.Duration) ([]IntDatapoint, error) {
	return g.QuerySince(q, ago).AsInts()
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryFloatsSince(q string, ago time.Duration) ([]FloatDatapoint, error) {
	return g.QuerySince(q, ago).AsFloats()
}

// Fetches a Graphite result. Deferring identifying whether the result are ints
// of floats to later. Useful in clients that executes adhoc queries.
func (g *Client) Query(q string, interval TimeInterval) Datapoints {
	if err := interval.Check(); err != nil {
		return Datapoints{err, nil}
	}

	// Cloning to be able to modify.
	url := g.url

	queryPart := constructQueryPart(q)
	queryPart.Add("from", graphiteDateFormat(interval.From))
	queryPart.Add("until", graphiteDateFormat(interval.To))
	url.RawQuery = queryPart.Encode()

	resp, err := g.Client.Get(url.String())
	if err != nil {
		return Datapoints{err, nil}
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Datapoints{err, nil}
	}

	return parseGraphiteResponse(body)
}

func (g *Client) QuerySince(q string, ago time.Duration) Datapoints {
	if ago.Nanoseconds() <= 0 {
		return Datapoints{errors.New("Duration is expected to be positive."), nil}
	}

	// Cloning to be able to modify.
	url := g.url

	queryPart := constructQueryPart(q)
	queryPart.Add("from", fmt.Sprintf("%dminutes", ago.Minutes()))
	url.RawQuery = queryPart.Encode()

	resp, err := http.Get(url.String())
	if err != nil {
		return Datapoints{err, nil}
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Datapoints{err, nil}
	}

	return parseGraphiteResponse(body)
}

func parseGraphiteResponse(body []byte) Datapoints {
	var dps Datapoints
	var res []target

	decoder := json.NewDecoder(bytes.NewBuffer(body))

	// Important to distinguish between ints and floats.
	decoder.UseNumber()

	dps.err = decoder.Decode(&res)
	if len(res) == 0 {
		dps.err = errors.New("Unexpected Graphite response. No targets were matched.")
	}
	if len(res) > 1 {
		dps.err = errors.New("Unexpected Graphite response. More than one target were returned.")
	}
	if dps.err != nil {
		return dps
	}

	// TODO: Check the query is the same as the target name.

	dps.points = res[0].Datapoints
	return dps
}

type queryResult []target

type target struct {
	Target string `json:"target"`

	// Datapoints are either
	//
	//     [[FLOAT, INT], ..., [FLOAT, INT]]  (type []intDatapoint).
	//
	// or
	//
	//     [[FLOAT, FLOAT], ..., [FLOAT, FLOAT]] (type []floatDatapoint).
	Datapoints [][]interface{} `json:"datapoints"`
}
<commit_msg>chore: remove unnecessary comment<commit_after>package infrastructure

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	httpurl "net/url"
	"time"
)

type Client struct {
	url    *httpurl.URL
	Client *http.Client
}

func New(url string) (*Client, error) {
	u, err := httpurl.Parse(url)
	if err != nil {
		return nil, err
	}
	return &Client{
		url:    u,
		Client: &http.Client{},
	}, nil
}

func NewFromURL(url httpurl.URL) *Client {
	return &Client{&url, &http.Client{}}
}

type TimeInterval struct {
	From time.Time
	To   time.Time
}

func (t *TimeInterval) Check() error {
	if t.From.After(t.To) {
		return errors.New("From must be before To.")
	}
	return nil
}

func graphiteDateFormat(t time.Time) string {
	return fmt.Sprintf("%02d:%02d_%d%02d%02d", t.Hour(), t.Minute(), t.Year(), t.Month(), t.Day())
}

type FloatDatapoint struct {
	Time  time.Time
	Value *float64
}

type IntDatapoint struct {
	Time  time.Time
	Value *int64
}

type Datapoints struct {
	// Previous error to make API nicer.
	err    error
	points [][]interface{}
}

func (d Datapoints) AsInts() ([]IntDatapoint, error) {
	if d.err != nil {
		return nil, d.err
	}

	points := make([]IntDatapoint, 0, len(d.points))
	for _, point := range d.points {
		jsonUnixTime, ok := point[1].(json.Number)
		if !ok {
			return nil, errors.New("Unix timestamp not number.")
		}
		unixTime, err := jsonUnixTime.Int64()
		if err != nil {
			return nil, errors.New("Unix time not proper number.")
		}

		var value *int64
		if point[0] != nil {
			jsonValue, ok := point[0].(json.Number)
			if !ok {
				return nil, errors.New("Value not a number.")
			}
			value = new(int64)
			*value, err = jsonValue.Int64()
			if err != nil {
				return nil, errors.New("Value not proper number.")
			}
		}
		points = append(points, IntDatapoint{time.Unix(unixTime, 0), value})
	}

	return points, nil
}

func (d Datapoints) AsFloats() ([]FloatDatapoint, error) {
	if d.err != nil {
		return nil, d.err
	}

	points := make([]FloatDatapoint, 0, len(d.points))
	for _, point := range d.points {
		jsonUnixTime, ok := point[1].(json.Number)
		if !ok {
			return nil, errors.New("Unix timestamp not number.")
		}
		unixTime, err := jsonUnixTime.Int64()
		if err != nil {
			return nil, errors.New("Unix time not proper number.")
		}

		var value *float64
		if point[0] != nil {
			jsonValue, ok := point[0].(json.Number)
			if !ok {
				return nil, errors.New("Value not a number.")
			}
			value = new(float64)
			*value, err = jsonValue.Float64()
			if err != nil {
				return nil, errors.New("Value not proper number.")
			}
		}
		points = append(points, FloatDatapoint{time.Unix(unixTime, 0), value})
	}

	return points, nil
}

func constructQueryPart(q string) httpurl.Values {
	query := make(httpurl.Values)
	query.Add("target", q)
	query.Add("format", "json")
	return query
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryInts(q string, interval TimeInterval) ([]IntDatapoint, error) {
	return g.Query(q, interval).AsInts()
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryFloats(q string, interval TimeInterval) ([]FloatDatapoint, error) {
	return g.Query(q, interval).AsFloats()
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryIntsSince(q string, ago time.Duration) ([]IntDatapoint, error) {
	return g.QuerySince(q, ago).AsInts()
}

// Helper method to make it easier to create an interface for Client.
func (g *Client) QueryFloatsSince(q string, ago time.Duration) ([]FloatDatapoint, error) {
	return g.QuerySince(q, ago).AsFloats()
}

// Fetches a Graphite result. Deferring identifying whether the result are ints
// of floats to later. Useful in clients that executes adhoc queries.
func (g *Client) Query(q string, interval TimeInterval) Datapoints {
	if err := interval.Check(); err != nil {
		return Datapoints{err, nil}
	}

	// Cloning to be able to modify.
	url := g.url

	queryPart := constructQueryPart(q)
	queryPart.Add("from", graphiteDateFormat(interval.From))
	queryPart.Add("until", graphiteDateFormat(interval.To))
	url.RawQuery = queryPart.Encode()

	resp, err := g.Client.Get(url.String())
	if err != nil {
		return Datapoints{err, nil}
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Datapoints{err, nil}
	}

	return parseGraphiteResponse(body)
}

func (g *Client) QuerySince(q string, ago time.Duration) Datapoints {
	if ago.Nanoseconds() <= 0 {
		return Datapoints{errors.New("Duration is expected to be positive."), nil}
	}

	// Cloning to be able to modify.
	url := g.url

	queryPart := constructQueryPart(q)
	queryPart.Add("from", fmt.Sprintf("%dminutes", ago.Minutes()))
	url.RawQuery = queryPart.Encode()

	resp, err := http.Get(url.String())
	if err != nil {
		return Datapoints{err, nil}
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Datapoints{err, nil}
	}

	return parseGraphiteResponse(body)
}

func parseGraphiteResponse(body []byte) Datapoints {
	var dps Datapoints
	var res []target

	decoder := json.NewDecoder(bytes.NewBuffer(body))

	// Important to distinguish between ints and floats.
	decoder.UseNumber()

	dps.err = decoder.Decode(&res)
	if len(res) == 0 {
		dps.err = errors.New("Unexpected Graphite response. No targets were matched.")
	}
	if len(res) > 1 {
		dps.err = errors.New("Unexpected Graphite response. More than one target were returned.")
	}
	if dps.err != nil {
		return dps
	}

	dps.points = res[0].Datapoints
	return dps
}

type queryResult []target

type target struct {
	Target string `json:"target"`

	// Datapoints are either
	//
	//     [[FLOAT, INT], ..., [FLOAT, INT]]  (type []intDatapoint).
	//
	// or
	//
	//     [[FLOAT, FLOAT], ..., [FLOAT, FLOAT]] (type []floatDatapoint).
	Datapoints [][]interface{} `json:"datapoints"`
}
<|endoftext|>


<commit_before>// Copyright (c) 2020 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ut_test

import (
	"encoding/binary"
	"fmt"
	"net"
	"reflect"
	"runtime"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"

	"github.com/projectcalico/felix/bpf"
	"github.com/projectcalico/felix/bpf/conntrack"
)

func TestMapEntryDeletion(t *testing.T) {
	k, err1 := setUpMapTestWithSingleKV(t)

	err2 := ctMap.Delete(k.AsBytes())
	err3 := ctMap.Delete(k.AsBytes())

	// Defer error checking since the Delete calls do the cleanup for this test...
	Expect(err1).NotTo(HaveOccurred(), "Failed to create map entry")
	Expect(err2).NotTo(HaveOccurred(), "Failed to delete map entry")
	Expect(bpf.IsNotExists(err3)).To(Equal(true), "Error from deletion of non-existent entry was incorrect")
}

func TestMapIterActionDelete(t *testing.T) {
	k, err1 := setUpMapTestWithSingleKV(t)

	seenKey := false
	iterErr := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKey = seenKey || reflect.DeepEqual(k2, k[:])
		return bpf.IterDelete
	})

	seenKeyAfterDel := false
	iterErr2 := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKeyAfterDel = seenKeyAfterDel || reflect.DeepEqual(k2, k[:])
		return bpf.IterNone
	})

	// Defer error checking since the Delete call does the cleanup for this test...
	Expect(err1).NotTo(HaveOccurred(), "Failed to create map entry")
	Expect(iterErr).ToNot(HaveOccurred(), "Failed to iterate map")
	Expect(seenKey).To(BeTrue(), "Expected to see the key we put in the map")
	Expect(seenKeyAfterDel).To(BeFalse(), "Saw key we'd just deleted")
	Expect(iterErr2).ToNot(HaveOccurred(), "Failed to iterate map after delete")
}

func TestMapIterationDeleteAfter(t *testing.T) {
	k, err1 := setUpMapTestWithSingleKV(t)

	seenKey := false
	iterErr := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKey = seenKey || reflect.DeepEqual(k2, k[:])
		return bpf.IterNone
	})

	err2 := ctMap.Delete(k.AsBytes())

	seenKeyAfterDel := false
	iterErr2 := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKeyAfterDel = seenKeyAfterDel || reflect.DeepEqual(k2, k[:])
		return bpf.IterNone
	})

	// Defer error checking since the Delete call does the cleanup for this test...
	Expect(err1).NotTo(HaveOccurred(), "Failed to create map entry")
	Expect(iterErr).ToNot(HaveOccurred(), "Failed to iterate map")
	Expect(seenKey).To(BeTrue(), "Expected to see the key we put in the map")
	Expect(seenKeyAfterDel).To(BeFalse(), "Saw key we'd just deleted")
	Expect(iterErr2).ToNot(HaveOccurred(), "Failed to iterate map after delete")
	Expect(err2).NotTo(HaveOccurred(), "Failed to delete map entry")
}

func setUpMapTestWithSingleKV(t *testing.T) (conntrack.Key, error) {
	RegisterTestingT(t)
	k := conntrack.NewKey(1, net.ParseIP("10.0.0.1"), 51234, net.ParseIP("10.0.0.2"), 8080)
	v := conntrack.Value{}
	for i := range v {
		v[i] = uint8(i)
	}
	err1 := ctMap.Update(k.AsBytes(), v[:])
	return k, err1
}

func BenchmarkMapIteration10k(b *testing.B) {
	benchMapIteration(b, 10000)
}

func BenchmarkMapIteration100k(b *testing.B) {
	benchMapIteration(b, 100000)
}

func BenchmarkMapIteration500k(b *testing.B) {
	benchMapIteration(b, 500000)
}

func benchMapIteration(b *testing.B, n int) {
	defer cleanUpMaps()
	logLevel := logrus.GetLevel()
	logrus.SetLevel(logrus.InfoLevel)
	defer logrus.SetLevel(logLevel)
	setUpConntrackMapEntries(b, n)
	var keepK, keepV []byte
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		err := ctMap.Iter(func(k, v []byte) bpf.IteratorAction {
			keepK = k
			keepV = v
			return bpf.IterNone
		})
		if err != nil {
			panic(err)
		}
	}
	runtime.KeepAlive(keepK)
	runtime.KeepAlive(keepV)
	b.StopTimer()
}

func setUpConntrackMapEntries(b *testing.B, n int) {
	b.StopTimer()
	for i := 0; i < n; i++ {
		var k conntrack.Key
		var v conntrack.Value
		binary.LittleEndian.PutUint32(k[:], uint32(i))
		err := ctMap.Update(k.AsBytes(), v[:])
		if err != nil {
			panic(err)
		}
	}
	b.StartTimer()
}

func BenchmarkMapIteratorMulti10(b *testing.B) {
	benchMapIteratorMulti(b, 10)
}

func BenchmarkMapIteratorMulti100(b *testing.B) {
	benchMapIteratorMulti(b, 100)
}

func BenchmarkMapIteratorMulti101(b *testing.B) {
	benchMapIteratorMulti(b, 101)
}

func BenchmarkMapIteratorMulti10k(b *testing.B) {
	benchMapIteratorMulti(b, 10000)
}

func BenchmarkMapIteratorMulti100k(b *testing.B) {
	benchMapIteratorMulti(b, 100000)
}

func BenchmarkMapIteratorMulti500k(b *testing.B) {
	benchMapIteratorMulti(b, 500000)
}

func benchMapIteratorMulti(b *testing.B, n int) {
	logLevel := logrus.GetLevel()
	logrus.SetLevel(logrus.InfoLevel)
	defer logrus.SetLevel(logLevel)
	setUpConntrackMapEntries(b, n)

	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		doSingleMapIteratorMultiTest(n)
	}
	b.StopTimer()
	cleanUpMaps()
}

func doSingleMapIteratorMultiTest(n int) {
	iter, err := bpf.NewMapIterator(ctMap.MapFD(), conntrack.KeySize, conntrack.ValueSize, conntrack.MaxEntries)
	if err != nil {
		panic(err)
	}

	numIterations := 0
	var k, v []byte
	for {
		k, v, err = iter.Next()
		if err != nil {
			if err == bpf.ErrIterationFinished {
				break
			}
			panic(err)
		}
		numIterations++
	}
	if numIterations != n {
		panic(fmt.Sprintf("Unexpected number of iterations: %d", numIterations))
	}
	_ = iter.Close()
	runtime.KeepAlive(k)
	runtime.KeepAlive(v)
}
<commit_msg>Add test for delete/add during iteration of BPF map<commit_after>// Copyright (c) 2020 Tigera, Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ut_test

import (
	"encoding/binary"
	"fmt"
	"net"
	"reflect"
	"runtime"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/sirupsen/logrus"

	"github.com/projectcalico/felix/bpf"
	"github.com/projectcalico/felix/bpf/conntrack"
)

func TestMapEntryDeletion(t *testing.T) {
	k, err1 := setUpMapTestWithSingleKV(t)

	err2 := ctMap.Delete(k.AsBytes())
	err3 := ctMap.Delete(k.AsBytes())

	// Defer error checking since the Delete calls do the cleanup for this test...
	Expect(err1).NotTo(HaveOccurred(), "Failed to create map entry")
	Expect(err2).NotTo(HaveOccurred(), "Failed to delete map entry")
	Expect(bpf.IsNotExists(err3)).To(Equal(true), "Error from deletion of non-existent entry was incorrect")
}

func TestMapIterActionDelete(t *testing.T) {
	k, err1 := setUpMapTestWithSingleKV(t)

	seenKey := false
	iterErr := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKey = seenKey || reflect.DeepEqual(k2, k[:])
		return bpf.IterDelete
	})

	seenKeyAfterDel := false
	iterErr2 := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKeyAfterDel = seenKeyAfterDel || reflect.DeepEqual(k2, k[:])
		return bpf.IterNone
	})

	// Defer error checking since the Delete call does the cleanup for this test...
	Expect(err1).NotTo(HaveOccurred(), "Failed to create map entry")
	Expect(iterErr).ToNot(HaveOccurred(), "Failed to iterate map")
	Expect(seenKey).To(BeTrue(), "Expected to see the key we put in the map")
	Expect(seenKeyAfterDel).To(BeFalse(), "Saw key we'd just deleted")
	Expect(iterErr2).ToNot(HaveOccurred(), "Failed to iterate map after delete")
}

func TestMapIterationDeleteAfter(t *testing.T) {
	k, err1 := setUpMapTestWithSingleKV(t)

	seenKey := false
	iterErr := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKey = seenKey || reflect.DeepEqual(k2, k[:])
		return bpf.IterNone
	})

	err2 := ctMap.Delete(k.AsBytes())

	seenKeyAfterDel := false
	iterErr2 := ctMap.Iter(func(k2, v []byte) bpf.IteratorAction {
		seenKeyAfterDel = seenKeyAfterDel || reflect.DeepEqual(k2, k[:])
		return bpf.IterNone
	})

	// Defer error checking since the Delete call does the cleanup for this test...
	Expect(err1).NotTo(HaveOccurred(), "Failed to create map entry")
	Expect(iterErr).ToNot(HaveOccurred(), "Failed to iterate map")
	Expect(seenKey).To(BeTrue(), "Expected to see the key we put in the map")
	Expect(seenKeyAfterDel).To(BeFalse(), "Saw key we'd just deleted")
	Expect(iterErr2).ToNot(HaveOccurred(), "Failed to iterate map after delete")
	Expect(err2).NotTo(HaveOccurred(), "Failed to delete map entry")
}

// TestDeleteDuringIter tries to validate that BPF map iteration is working as we want even when keys are being added
// to and deleted from the map during iteration.
func TestDeleteDuringIter(t *testing.T) {
	RegisterTestingT(t)
	defer cleanUpMaps()
	logLevel := logrus.GetLevel()
	defer func() {
		logrus.SetLevel(logLevel)
	}()
	logrus.SetLevel(logrus.InfoLevel)

	testDelDuringIterN(10)
	testDelDuringIterN(11)
	testDelDuringIterN(100)
	testDelDuringIterN(10000)
	testDelDuringIterN(100000)
}

func testDelDuringIterN(numEntries int) {
	cleanUpMaps()
	for i := 0; i < numEntries; i++ {
		err := insertNumberedKey(i)
		Expect(err).NotTo(HaveOccurred())
	}
	// First pass, no deletions.
	seenKeys := map[conntrack.Key]bool{}
	err := ctMap.Iter(func(k, v []byte) bpf.IteratorAction {
		key := conntrack.KeyFromBytes(k)
		Expect(seenKeys[key]).To(BeFalse(), "Saw a duplicate key")
		seenKeys[key] = true
		return bpf.IterNone
	})
	Expect(err).NotTo(HaveOccurred())
	Expect(seenKeys).To(HaveLen(numEntries), "Should have seen expected num entries on first iteration")

	// Second pass, delete alternate keys.
	seenKeys = map[conntrack.Key]bool{}
	deleteNextKey := false
	expectedKeys := map[conntrack.Key]bool{}
	err = ctMap.Iter(func(k, v []byte) bpf.IteratorAction {
		defer func() {
			deleteNextKey = !deleteNextKey
		}()
		key := conntrack.KeyFromBytes(k)
		Expect(seenKeys[key]).To(BeFalse(), "Saw a duplicate key")
		seenKeys[key] = true
		if deleteNextKey {
			return bpf.IterDelete
		} else {
			expectedKeys[key] = true
			return bpf.IterNone
		}
	})
	Expect(err).NotTo(HaveOccurred())
	Expect(seenKeys).To(HaveLen(numEntries), "Should have seen expected num entries on deletion iteration")

	// Third pass, insert key on each iteration an delete on alternate iterations.
	numLeftAfterSecondPass := len(expectedKeys)
	seenKeys = map[conntrack.Key]bool{}
	deleteNextKey = false
	insertClock := 0
	insertIdx := numEntries
	numInsertedInThirdPass := 0
	err = ctMap.Iter(func(k, v []byte) bpf.IteratorAction {
		defer func() {
			deleteNextKey = !deleteNextKey
			insertClock++
		}()
		key := conntrack.KeyFromBytes(k)
		Expect(seenKeys[key]).To(BeFalse(), "Saw a duplicate key")
		seenKeys[key] = true
		delete(expectedKeys, key)
		if insertClock % 3 == 0 {
			err := insertNumberedKey(insertIdx)
			Expect(err).NotTo(HaveOccurred())
			insertIdx++
			numInsertedInThirdPass++
		}
		if deleteNextKey {
			return bpf.IterDelete
		} else {
			return bpf.IterNone
		}
	})
	Expect(err).NotTo(HaveOccurred())
	Expect(expectedKeys).To(BeEmpty(), "Didn't see every key in third pass")
	Expect(len(seenKeys)).To(BeNumerically(">=", numLeftAfterSecondPass),
		"Should see at least as many entries as we left in the second pass")
	Expect(len(seenKeys)).To(BeNumerically("<=", numLeftAfterSecondPass+numInsertedInThirdPass),
		"Should see all of the numLeftAfterSecondPass and some of the numInsertedInThirdPass")
	logrus.WithField("numSeen", len(seenKeys)).Info("Saw this many keys")
}

func setUpMapTestWithSingleKV(t *testing.T) (conntrack.Key, error) {
	RegisterTestingT(t)
	k := conntrack.NewKey(1, net.ParseIP("10.0.0.1"), 51234, net.ParseIP("10.0.0.2"), 8080)
	v := conntrack.Value{}
	for i := range v {
		v[i] = uint8(i)
	}
	err1 := ctMap.Update(k.AsBytes(), v[:])
	return k, err1
}

func insertNumberedKey(n int) error {
	k := conntrack.NewKey(1, net.ParseIP("10.0.0.1"), uint16(n), net.ParseIP("10.0.0.2"), uint16(n>>16))
	v := conntrack.Value{}
	for i := range v {
		v[i] = uint8(i+n)
	}
	return ctMap.Update(k.AsBytes(), v[:])
}

func BenchmarkMapIteration10k(b *testing.B) {
	benchMapIteration(b, 10000)
}

func BenchmarkMapIteration100k(b *testing.B) {
	benchMapIteration(b, 100000)
}

func BenchmarkMapIteration500k(b *testing.B) {
	benchMapIteration(b, 500000)
}

func benchMapIteration(b *testing.B, n int) {
	defer cleanUpMaps()
	logLevel := logrus.GetLevel()
	logrus.SetLevel(logrus.InfoLevel)
	defer logrus.SetLevel(logLevel)
	setUpConntrackMapEntries(b, n)
	var keepK, keepV []byte
	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		err := ctMap.Iter(func(k, v []byte) bpf.IteratorAction {
			keepK = k
			keepV = v
			return bpf.IterNone
		})
		if err != nil {
			panic(err)
		}
	}
	runtime.KeepAlive(keepK)
	runtime.KeepAlive(keepV)
	b.StopTimer()
}

func setUpConntrackMapEntries(b *testing.B, n int) {
	b.StopTimer()
	for i := 0; i < n; i++ {
		var k conntrack.Key
		var v conntrack.Value
		binary.LittleEndian.PutUint32(k[:], uint32(i))
		err := ctMap.Update(k.AsBytes(), v[:])
		if err != nil {
			panic(err)
		}
	}
	b.StartTimer()
}

func BenchmarkMapIteratorMulti10(b *testing.B) {
	benchMapIteratorMulti(b, 10)
}

func BenchmarkMapIteratorMulti100(b *testing.B) {
	benchMapIteratorMulti(b, 100)
}

func BenchmarkMapIteratorMulti101(b *testing.B) {
	benchMapIteratorMulti(b, 101)
}

func BenchmarkMapIteratorMulti10k(b *testing.B) {
	benchMapIteratorMulti(b, 10000)
}

func BenchmarkMapIteratorMulti100k(b *testing.B) {
	benchMapIteratorMulti(b, 100000)
}

func BenchmarkMapIteratorMulti500k(b *testing.B) {
	benchMapIteratorMulti(b, 500000)
}

func benchMapIteratorMulti(b *testing.B, n int) {
	logLevel := logrus.GetLevel()
	logrus.SetLevel(logrus.InfoLevel)
	defer logrus.SetLevel(logLevel)
	setUpConntrackMapEntries(b, n)

	b.ReportAllocs()
	for i := 0; i < b.N; i++ {
		doSingleMapIteratorMultiTest(n)
	}
	b.StopTimer()
	cleanUpMaps()
}

func doSingleMapIteratorMultiTest(n int) {
	iter, err := bpf.NewMapIterator(ctMap.MapFD(), conntrack.KeySize, conntrack.ValueSize, conntrack.MaxEntries)
	if err != nil {
		panic(err)
	}

	numIterations := 0
	var k, v []byte
	for {
		k, v, err = iter.Next()
		if err != nil {
			if err == bpf.ErrIterationFinished {
				break
			}
			panic(err)
		}
		numIterations++
	}
	if numIterations != n {
		panic(fmt.Sprintf("Unexpected number of iterations: %d", numIterations))
	}
	_ = iter.Close()
	runtime.KeepAlive(k)
	runtime.KeepAlive(v)
}
<|endoftext|>


<commit_before>package pieql

import (
	"io"
)

// Parser represents a PieQL parser.
type Parser struct {
	s *Scanner
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// Parse parses the next statement from the underlying reader.
func (p *Parser) Parse() (*SelectStatement, error) {
	panic("aaaaaaah!!!!")
}
<commit_msg>add parse for SELECT, field, FROM, and source<commit_after>package pieql

import (
	"fmt"
	"io"
)

// Parser represents a PieQL parser.
type Parser struct {
	s   *Scanner
	buf struct {
		tok Token  // last read token
		lit string // last read literal
		n   int    // buffer size
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// Parse parses the next statement from the underlying reader.
func (p *Parser) Parse() (*SelectStatement, error) {
	stmt := &SelectStatement{}

	if tok, lit := p.scanIgnoreWhitespace(); tok != SELECT {
		return nil, fmt.Errorf("found %q, expected SELECT", lit)
	}

	for {
		// Read a field.
		tok, lit := p.scanIgnoreWhitespace()
		if tok != IDENT && tok != MUL {
			return nil, fmt.Errorf("found %q, expected field", lit)
		}
		stmt.Fields = append(stmt.Fields, &Field{lit})

		// If the next token is not a comma then break the loop.
		if tok, _ := p.scanIgnoreWhitespace(); tok != COMMA {
			p.unscan()
			break
		}
	}

	// Next we expect to see the "FROM" keyword.
	if tok, lit := p.scanIgnoreWhitespace(); tok != FROM {
		return nil, fmt.Errorf("found %q, expected FROM", lit)
	}

	// Finally read in the source name.
	tok, lit := p.scanIgnoreWhitespace()
	if tok != IDENT {
		return nil, fmt.Errorf("found %q, expected table name", lit)
	}
	stmt.Source = lit

	return stmt, nil
}

// scan returns the next token from the scanner.
// If a token been unscanned, read that instead.
func (p *Parser) scan() (tok Token, lit string) {
	// If we have a token on the buffer, return it.
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	// Otherwise read the next token from the scanner.
	tok, lit = p.s.Scan()

	// Save it to the buffer in case we need to unscan later.
	p.buf.tok, p.buf.lit = tok, lit

	return

}

// unscan pushes the previously read token back onto the buffer
func (p *Parser) unscan() { p.buf.n = 1 }

// scanIgnoreWhitespace scans the next non-whitespace token.
func (p *Parser) scanIgnoreWhitespace() (tok Token, lit string) {
	tok, lit = p.scan()
	if tok == WS {
		tok, lit = p.scan()
	}
	return
}
<|endoftext|>