<commit_before>package gormigrate

import (
	"errors"
	"fmt"

	"github.com/jinzhu/gorm"
)

// MigrateFunc is the func signature for migrating.
type MigrateFunc func(*gorm.DB) error

// RollbackFunc is the func signature for rollbacking.
type RollbackFunc func(*gorm.DB) error

// InitSchemaFunc is the func signature for initializing the schema.
type InitSchemaFunc func(*gorm.DB) error

// Options define options for all migrations.
type Options struct {
	// TableName is the migration table.
	TableName string
	// IDColumnName is the name of column where the migration id will be stored.
	IDColumnName string
	// IDColumnSize is the length of the migration id column
	IDColumnSize int
	// UseTransaction makes Gormigrate execute migrations inside a single transaction.
	// Keep in mind that not all databases support DDL commands inside transactions.
	UseTransaction bool
}

// Migration represents a database migration (a modification to be made on the database).
type Migration struct {
	// ID is the migration identifier. Usually a timestamp like "201601021504".
	ID string
	// Migrate is a function that will br executed while running this migration.
	Migrate MigrateFunc
	// Rollback will be executed on rollback. Can be nil.
	Rollback RollbackFunc
}

// Gormigrate represents a collection of all migrations of a database schema.
type Gormigrate struct {
	db         *gorm.DB
	tx         *gorm.DB
	options    *Options
	migrations []*Migration
	initSchema InitSchemaFunc
}

// DuplicatedIDError is returned when more than one migration have the same ID
type DuplicatedIDError struct {
	ID string
}

func (e *DuplicatedIDError) Error() string {
	return fmt.Sprintf(`Duplicated migration ID: "%s"`, e.ID)
}

var (
	// DefaultOptions can be used if you don't want to think about options.
	DefaultOptions = &Options{
		TableName:      "migrations",
		IDColumnName:   "id",
		IDColumnSize:   255,
		UseTransaction: false,
	}

	// ErrRollbackImpossible is returned when trying to rollback a migration
	// that has no rollback function.
	ErrRollbackImpossible = errors.New("It's impossible to rollback this migration")

	// ErrNoMigrationDefined is returned when no migration is defined.
	ErrNoMigrationDefined = errors.New("No migration defined")

	// ErrMissingID is returned when the ID od migration is equal to ""
	ErrMissingID = errors.New("Missing ID in migration")

	// ErrNoRunnedMigration is returned when any runned migration was found while
	// running RollbackLast
	ErrNoRunnedMigration = errors.New("Could not find last runned migration")
)

// New returns a new Gormigrate.
func New(db *gorm.DB, options *Options, migrations []*Migration) *Gormigrate {
	if options.TableName == "" {
		options.TableName = DefaultOptions.TableName
	}
	if options.IDColumnName == "" {
		options.IDColumnName = DefaultOptions.IDColumnName
	}
	if options.IDColumnSize == 0 {
		options.IDColumnSize = DefaultOptions.IDColumnSize
	}
	return &Gormigrate{
		db:         db,
		options:    options,
		migrations: migrations,
	}
}

// InitSchema sets a function that is run if no migration is found.
// The idea is preventing to run all migrations when a new clean database
// is being migrating. In this function you should create all tables and
// foreign key necessary to your application.
func (g *Gormigrate) InitSchema(initSchema InitSchemaFunc) {
	g.initSchema = initSchema
}

// Migrate executes all migrations that did not run yet.
func (g *Gormigrate) Migrate() error {
	return g.migrate("")
}

// Migrate executes all migrations that did not run yet up to the migration that matches `migrationId`.
func (g *Gormigrate) MigrateTo(migrationId string) error {
	return g.migrate(migrationId)
}

func (g *Gormigrate) migrate(migrationId string) error {
	if err := g.checkDuplicatedID(); err != nil {
		return err
	}

	if err := g.createMigrationTableIfNotExists(); err != nil {
		return err
	}

	g.begin()

	if g.initSchema != nil && g.isFirstRun() {
		if err := g.runInitSchema(); err != nil {
			g.rollback()
			return err
		}
		return g.commit()
	}

	for _, migration := range g.migrations {
		if err := g.runMigration(migration); err != nil {
			g.rollback()
			return err
		}
		if migrationId != "" && migration.ID == migrationId {
			break
		}
	}

	return g.commit()
}

func (g *Gormigrate) checkDuplicatedID() error {
	lookup := make(map[string]struct{}, len(g.migrations))
	for _, m := range g.migrations {
		if _, ok := lookup[m.ID]; ok {
			return &DuplicatedIDError{ID: m.ID}
		}
		lookup[m.ID] = struct{}{}
	}
	return nil
}

// RollbackLast undo the last migration
func (g *Gormigrate) RollbackLast() error {
	if len(g.migrations) == 0 {
		return ErrNoMigrationDefined
	}

	lastRunnedMigration, err := g.getLastRunnedMigration()
	if err != nil {
		return err
	}

	if err := g.RollbackMigration(lastRunnedMigration); err != nil {
		return err
	}
	return nil
}

// RollbackTo undoes migrations up to the given migration that matches the `migrationId`.
// Migration with the matching `migrationId` is not rolled back.
func (g *Gormigrate) RollbackTo(migrationId string) error {
	if len(g.migrations) == 0 {
		return ErrNoMigrationDefined
	}

	g.begin()

	for i := len(g.migrations) - 1; i >= 0; i-- {
		migration := g.migrations[i]
		if migration.ID == migrationId {
			break
		}
		if g.migrationDidRun(migration) {
			if err := g.rollbackMigration(migration, false); err != nil {
				g.rollback()
				return err
			}
		}
	}

	return g.commit()
}

func (g *Gormigrate) getLastRunnedMigration() (*Migration, error) {
	for i := len(g.migrations) - 1; i >= 0; i-- {
		migration := g.migrations[i]
		if g.migrationDidRun(migration) {
			return migration, nil
		}
	}
	return nil, ErrNoRunnedMigration
}

// RollbackMigration undo a migration.
func (g *Gormigrate) RollbackMigration(m *Migration) error {
	return g.rollbackMigration(m, true)
}

// rollbackMigration rolls back the given transaction. If `withTransaction` is true, then the operation is wrapped in
// transaction (if g.options.UseTransaction is also true).
// Call rollbackMigration with `withTransaction` false if you want to wrap multiple operations in a single transaction.
func (g *Gormigrate) rollbackMigration(m *Migration, withTransaction bool) error {
	if m.Rollback == nil {
		return ErrRollbackImpossible
	}

	if withTransaction {
		g.begin()
	}

	if err := m.Rollback(g.tx); err != nil {
		return err
	}

	sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", g.options.TableName, g.options.IDColumnName)
	if err := g.db.Exec(sql, m.ID).Error; err != nil {
		if withTransaction {
			g.rollback()
		}
		return err
	}

	if withTransaction {
		return g.commit()
	}
	return nil
}

func (g *Gormigrate) runInitSchema() error {
	if err := g.initSchema(g.tx); err != nil {
		return err
	}

	for _, migration := range g.migrations {
		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}

	return nil
}

func (g *Gormigrate) runMigration(migration *Migration) error {
	if len(migration.ID) == 0 {
		return ErrMissingID
	}

	if !g.migrationDidRun(migration) {
		if err := migration.Migrate(g.tx); err != nil {
			return err
		}

		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) createMigrationTableIfNotExists() error {
	if g.db.HasTable(g.options.TableName) {
		return nil
	}

	sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(%d) PRIMARY KEY)", g.options.TableName, g.options.IDColumnName, g.options.IDColumnSize)
	if err := g.db.Exec(sql).Error; err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) migrationDidRun(m *Migration) bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Where(fmt.Sprintf("%s = ?", g.options.IDColumnName), m.ID).
		Count(&count)
	return count > 0
}

func (g *Gormigrate) isFirstRun() bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Count(&count)
	return count == 0
}

func (g *Gormigrate) insertMigration(id string) error {
	sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", g.options.TableName, g.options.IDColumnName)
	return g.tx.Exec(sql, id).Error
}

func (g *Gormigrate) begin() {
	if g.options.UseTransaction {
		g.tx = g.db.Begin()
	} else {
		g.tx = g.db
	}
}

func (g *Gormigrate) commit() error {
	if g.options.UseTransaction {
		if err := g.tx.Commit().Error; err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) rollback() {
	if g.options.UseTransaction {
		g.tx.Rollback()
	}
}
<commit_msg>Remove "withTransaction" flag<commit_after>package gormigrate

import (
	"errors"
	"fmt"

	"github.com/jinzhu/gorm"
)

// MigrateFunc is the func signature for migrating.
type MigrateFunc func(*gorm.DB) error

// RollbackFunc is the func signature for rollbacking.
type RollbackFunc func(*gorm.DB) error

// InitSchemaFunc is the func signature for initializing the schema.
type InitSchemaFunc func(*gorm.DB) error

// Options define options for all migrations.
type Options struct {
	// TableName is the migration table.
	TableName string
	// IDColumnName is the name of column where the migration id will be stored.
	IDColumnName string
	// IDColumnSize is the length of the migration id column
	IDColumnSize int
	// UseTransaction makes Gormigrate execute migrations inside a single transaction.
	// Keep in mind that not all databases support DDL commands inside transactions.
	UseTransaction bool
}

// Migration represents a database migration (a modification to be made on the database).
type Migration struct {
	// ID is the migration identifier. Usually a timestamp like "201601021504".
	ID string
	// Migrate is a function that will br executed while running this migration.
	Migrate MigrateFunc
	// Rollback will be executed on rollback. Can be nil.
	Rollback RollbackFunc
}

// Gormigrate represents a collection of all migrations of a database schema.
type Gormigrate struct {
	db         *gorm.DB
	tx         *gorm.DB
	options    *Options
	migrations []*Migration
	initSchema InitSchemaFunc
}

// DuplicatedIDError is returned when more than one migration have the same ID
type DuplicatedIDError struct {
	ID string
}

func (e *DuplicatedIDError) Error() string {
	return fmt.Sprintf(`Duplicated migration ID: "%s"`, e.ID)
}

var (
	// DefaultOptions can be used if you don't want to think about options.
	DefaultOptions = &Options{
		TableName:      "migrations",
		IDColumnName:   "id",
		IDColumnSize:   255,
		UseTransaction: false,
	}

	// ErrRollbackImpossible is returned when trying to rollback a migration
	// that has no rollback function.
	ErrRollbackImpossible = errors.New("It's impossible to rollback this migration")

	// ErrNoMigrationDefined is returned when no migration is defined.
	ErrNoMigrationDefined = errors.New("No migration defined")

	// ErrMissingID is returned when the ID od migration is equal to ""
	ErrMissingID = errors.New("Missing ID in migration")

	// ErrNoRunnedMigration is returned when any runned migration was found while
	// running RollbackLast
	ErrNoRunnedMigration = errors.New("Could not find last runned migration")
)

// New returns a new Gormigrate.
func New(db *gorm.DB, options *Options, migrations []*Migration) *Gormigrate {
	if options.TableName == "" {
		options.TableName = DefaultOptions.TableName
	}
	if options.IDColumnName == "" {
		options.IDColumnName = DefaultOptions.IDColumnName
	}
	if options.IDColumnSize == 0 {
		options.IDColumnSize = DefaultOptions.IDColumnSize
	}
	return &Gormigrate{
		db:         db,
		options:    options,
		migrations: migrations,
	}
}

// InitSchema sets a function that is run if no migration is found.
// The idea is preventing to run all migrations when a new clean database
// is being migrating. In this function you should create all tables and
// foreign key necessary to your application.
func (g *Gormigrate) InitSchema(initSchema InitSchemaFunc) {
	g.initSchema = initSchema
}

// Migrate executes all migrations that did not run yet.
func (g *Gormigrate) Migrate() error {
	return g.migrate("")
}

// Migrate executes all migrations that did not run yet up to the migration that matches `migrationId`.
func (g *Gormigrate) MigrateTo(migrationId string) error {
	return g.migrate(migrationId)
}

func (g *Gormigrate) migrate(migrationId string) error {
	if err := g.checkDuplicatedID(); err != nil {
		return err
	}

	if err := g.createMigrationTableIfNotExists(); err != nil {
		return err
	}

	g.begin()

	if g.initSchema != nil && g.isFirstRun() {
		if err := g.runInitSchema(); err != nil {
			g.rollback()
			return err
		}
		return g.commit()
	}

	for _, migration := range g.migrations {
		if err := g.runMigration(migration); err != nil {
			g.rollback()
			return err
		}
		if migrationId != "" && migration.ID == migrationId {
			break
		}
	}

	return g.commit()
}

func (g *Gormigrate) checkDuplicatedID() error {
	lookup := make(map[string]struct{}, len(g.migrations))
	for _, m := range g.migrations {
		if _, ok := lookup[m.ID]; ok {
			return &DuplicatedIDError{ID: m.ID}
		}
		lookup[m.ID] = struct{}{}
	}
	return nil
}

// RollbackLast undo the last migration
func (g *Gormigrate) RollbackLast() error {
	if len(g.migrations) == 0 {
		return ErrNoMigrationDefined
	}

	lastRunnedMigration, err := g.getLastRunnedMigration()
	if err != nil {
		return err
	}

	if err := g.RollbackMigration(lastRunnedMigration); err != nil {
		return err
	}
	return nil
}

// RollbackTo undoes migrations up to the given migration that matches the `migrationId`.
// Migration with the matching `migrationId` is not rolled back.
func (g *Gormigrate) RollbackTo(migrationId string) error {
	if len(g.migrations) == 0 {
		return ErrNoMigrationDefined
	}

	g.begin()

	for i := len(g.migrations) - 1; i >= 0; i-- {
		migration := g.migrations[i]
		if migration.ID == migrationId {
			break
		}
		if g.migrationDidRun(migration) {
			if err := g.rollbackMigration(migration); err != nil {
				g.rollback()
				return err
			}
		}
	}

	return g.commit()
}

func (g *Gormigrate) getLastRunnedMigration() (*Migration, error) {
	for i := len(g.migrations) - 1; i >= 0; i-- {
		migration := g.migrations[i]
		if g.migrationDidRun(migration) {
			return migration, nil
		}
	}
	return nil, ErrNoRunnedMigration
}

// RollbackMigration undo a migration.
func (g *Gormigrate) RollbackMigration(m *Migration) error {
	g.begin()
	if err := g.rollbackMigration(m); err != nil {
		g.rollback()
		return err
	}
	return g.commit()
}

func (g *Gormigrate) rollbackMigration(m *Migration) error {
	if m.Rollback == nil {
		return ErrRollbackImpossible
	}

	if err := m.Rollback(g.tx); err != nil {
		return err
	}

	sql := fmt.Sprintf("DELETE FROM %s WHERE %s = ?", g.options.TableName, g.options.IDColumnName)
	if err := g.db.Exec(sql, m.ID).Error; err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) runInitSchema() error {
	if err := g.initSchema(g.tx); err != nil {
		return err
	}

	for _, migration := range g.migrations {
		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}

	return nil
}

func (g *Gormigrate) runMigration(migration *Migration) error {
	if len(migration.ID) == 0 {
		return ErrMissingID
	}

	if !g.migrationDidRun(migration) {
		if err := migration.Migrate(g.tx); err != nil {
			return err
		}

		if err := g.insertMigration(migration.ID); err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) createMigrationTableIfNotExists() error {
	if g.db.HasTable(g.options.TableName) {
		return nil
	}

	sql := fmt.Sprintf("CREATE TABLE %s (%s VARCHAR(%d) PRIMARY KEY)", g.options.TableName, g.options.IDColumnName, g.options.IDColumnSize)
	if err := g.db.Exec(sql).Error; err != nil {
		return err
	}
	return nil
}

func (g *Gormigrate) migrationDidRun(m *Migration) bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Where(fmt.Sprintf("%s = ?", g.options.IDColumnName), m.ID).
		Count(&count)
	return count > 0
}

func (g *Gormigrate) isFirstRun() bool {
	var count int
	g.db.
		Table(g.options.TableName).
		Count(&count)
	return count == 0
}

func (g *Gormigrate) insertMigration(id string) error {
	sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?)", g.options.TableName, g.options.IDColumnName)
	return g.tx.Exec(sql, id).Error
}

func (g *Gormigrate) begin() {
	if g.options.UseTransaction {
		g.tx = g.db.Begin()
	} else {
		g.tx = g.db
	}
}

func (g *Gormigrate) commit() error {
	if g.options.UseTransaction {
		if err := g.tx.Commit().Error; err != nil {
			return err
		}
	}
	return nil
}

func (g *Gormigrate) rollback() {
	if g.options.UseTransaction {
		g.tx.Rollback()
	}
}
<|endoftext|>


<commit_before>package go_mocket

import (
	"database/sql"
	"log"
	"testing"
)

var DB *sql.DB

func GetUsers(db *sql.DB) []map[string]interface{} {
	var res []map[string]interface{}
	age := 27
	rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		var name string
		var age string
		if err := rows.Scan(&name, &age); err != nil {
			log.Fatal(err)
		}
		row := make(map[string]interface{})
		row["name"] = name
		row["age"] = age
		res = append(res, row)
	}
	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}
	return res
}

func TestResponses(t *testing.T) {
	sql.Register("fake_test", FakeDriver{})
	db, _ := sql.Open("fake_test", "connection_string") // Could be any connection string
	DB = db

	t.Run("Simple SELECT caught by query", func(t *testing.T) {
		Catcher.Logging = false
		commonReply := []map[string]interface{}{{"name": "FirstLast", "age": "30"}}
		Catcher.Reset().NewMock().WithQuery(`SELECT name FROM users WHERE`).WithReply(commonReply)
		result := GetUsers(DB)
		if len(result) != 1 {
			t.Errorf("Returned sets is not equal to 1. Received %d", len(result))
		}
		if result[0]["age"] != "30" {
			t.Errorf("Age is not equal. Got %v", result[0]["age"])
		}
	})
}
<commit_msg>* added tests<commit_after>package go_mocket

import (
	"database/sql"
	"log"
	"testing"
)

var DB *sql.DB

func GetUsers(db *sql.DB) []map[string]string {
	var res []map[string]string
	age := 27
	rows, err := db.Query("SELECT name FROM users WHERE age=?", age)
	if err != nil {
		log.Fatal(err)
	}
	defer rows.Close()
	for rows.Next() {
		var name string
		var age string
		if err := rows.Scan(&name, &age); err != nil {
			log.Fatal(err)
		}
		row := map[string]string{"name": name, "age": age}
		res = append(res, row)
	}
	if err := rows.Err(); err != nil {
		log.Fatal(err)
	}
	return res
}

func GetUsersWithError(db *sql.DB) error {
	age := 27
	_, err := db.Query("SELECT name FROM users WHERE age=?", age)
	return err
}

func CreateUsersWithError(db *sql.DB) error {
	age := 27
	_, err := db.Query("INSERT INTO users (age) VALUES (?) ", age)
	return err
}

func TestResponses(t *testing.T) {
	sql.Register("fake_test", FakeDriver{})
	db, _ := sql.Open("fake_test", "connection_string") // Could be any connection string
	DB = db
	commonReply := []map[string]interface{}{{"name": "FirstLast", "age": "30"}}

	t.Run("Simple SELECT caught by query", func(t *testing.T) {
		Catcher.Logging = false
		Catcher.Reset().NewMock().WithQuery(`SELECT name FROM users WHERE`).WithReply(commonReply)
		result := GetUsers(DB)
		if len(result) != 1 {
			t.Errorf("Returned sets is not equal to 1. Received %d", len(result))
		}
		if result[0]["age"] != "30" {
			t.Errorf("Age is not equal. Got %v", result[0]["age"])
		}
	})

	t.Run("Simple SELECT with direct object", func(t *testing.T) {
		t.Run("Not a once", func(t *testing.T) {
			Catcher.Reset()
			Catcher.Attach([]*FakeResponse{
				{
					Pattern:"SELECT name FROM users WHERE",
					Response: commonReply,
					Once: false,
				},
			})
			result := GetUsers(DB)
			if len(result) != 1 {
				t.Errorf("Returned sets is not equal to 1. Received %d", len(result))
			}
			if result[0]["age"] != "30" {
				t.Errorf("Age is not equal. Got %v", result[0]["age"])
			}
		})

		t.Run("Once", func(t *testing.T) {
			Catcher.Reset()
			Catcher.Attach([]*FakeResponse{
				{
					Pattern:"SELECT name FROM users WHERE",
					Response: commonReply,
					Once: true,
				},
			})
			GetUsers(DB) // Trigger once to use this mock
			result := GetUsers(DB) // trigger second time to receive empty results
			if len(result) != 0 {
				t.Errorf("Returned sets is not equal to 0. Received %d", len(result))
			}
		})
	})

	t.Run("Catch by arguments", func(t *testing.T) {
		Catcher.Reset().NewMock().WithArgs(int64(27)).WithReply(commonReply)
		result := GetUsers(DB)
		if len(result) != 1 {
			t.Fatalf("Returned sets is not equal to 1. Received %d", len(result))
		}
		if result[0]["age"] != "30" {
			t.Errorf("Age is not equal. Got %v", result[0]["age"])
		}
	})

	t.Run("Exceptions", func(t *testing.T) {
		t.Run("Fire Query error", func(t *testing.T) {
			Catcher.Reset().NewMock().WithArgs(int64(27)).WithReply(commonReply).WithQueryException()
			err := GetUsersWithError(DB)
			if err == nil {
				t.Fatal("Error not triggered")
			}
		})
		t.Run("Fire Execute error", func(t *testing.T) {
			Catcher.Reset().NewMock().WithQuery("INSERT INTO users (age)").WithQueryException()
			err := CreateUsersWithError(DB)
			if err == nil {
				t.Fatal("Error not triggered")
			}
		})
	})
}
<|endoftext|>


<commit_before>package registry

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

	"github.com/docker/docker/utils"
)

var (
	IMAGE_ID = "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d"
	TOKEN    = []string{"fake-token"}
	REPO     = "foo42/bar"
)

func spawnTestRegistrySession(t *testing.T) *Session {
	authConfig := &AuthConfig{}
	r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), makeURL("/v1/"), true)
	if err != nil {
		t.Fatal(err)
	}
	return r
}

func TestPingRegistryEndpoint(t *testing.T) {
	regInfo, err := pingRegistryEndpoint(makeURL("/v1/"))
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, regInfo.Standalone, true, "Expected standalone to be true (default)")
}

func TestGetRemoteHistory(t *testing.T) {
	r := spawnTestRegistrySession(t)
	hist, err := r.GetRemoteHistory(IMAGE_ID, makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, len(hist), 2, "Expected 2 images in history")
	assertEqual(t, hist[0], IMAGE_ID, "Expected "+IMAGE_ID+"as first ancestry")
	assertEqual(t, hist[1], "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
		"Unexpected second ancestry")
}

func TestLookupRemoteImage(t *testing.T) {
	r := spawnTestRegistrySession(t)
	found := r.LookupRemoteImage(IMAGE_ID, makeURL("/v1/"), TOKEN)
	assertEqual(t, found, true, "Expected remote lookup to succeed")
	found = r.LookupRemoteImage("abcdef", makeURL("/v1/"), TOKEN)
	assertEqual(t, found, false, "Expected remote lookup to fail")
}

func TestGetRemoteImageJSON(t *testing.T) {
	r := spawnTestRegistrySession(t)
	json, size, err := r.GetRemoteImageJSON(IMAGE_ID, makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, size, 154, "Expected size 154")
	if len(json) <= 0 {
		t.Fatal("Expected non-empty json")
	}

	_, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/"), TOKEN)
	if err == nil {
		t.Fatal("Expected image not found error")
	}
}

func TestGetRemoteImageLayer(t *testing.T) {
	r := spawnTestRegistrySession(t)
	data, err := r.GetRemoteImageLayer(IMAGE_ID, makeURL("/v1/"), TOKEN, 0)
	if err != nil {
		t.Fatal(err)
	}
	if data == nil {
		t.Fatal("Expected non-nil data result")
	}

	_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), TOKEN, 0)
	if err == nil {
		t.Fatal("Expected image not found error")
	}
}

func TestGetRemoteTags(t *testing.T) {
	r := spawnTestRegistrySession(t)
	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO, TOKEN)
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, len(tags), 1, "Expected one tag")
	assertEqual(t, tags["latest"], IMAGE_ID, "Expected tag latest to map to "+IMAGE_ID)

	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz", TOKEN)
	if err == nil {
		t.Fatal("Expected error when fetching tags for bogus repo")
	}
}

func TestGetRepositoryData(t *testing.T) {
	r := spawnTestRegistrySession(t)
	parsedUrl, err := url.Parse(makeURL("/v1/"))
	if err != nil {
		t.Fatal(err)
	}
	host := "http://" + parsedUrl.Host + "/v1/"
	data, err := r.GetRepositoryData("foo42/bar")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, len(data.ImgList), 2, "Expected 2 images in ImgList")
	assertEqual(t, len(data.Endpoints), 2,
		fmt.Sprintf("Expected 2 endpoints in Endpoints, found %d instead", len(data.Endpoints)))
	assertEqual(t, data.Endpoints[0], host,
		fmt.Sprintf("Expected first endpoint to be %s but found %s instead", host, data.Endpoints[0]))
	assertEqual(t, data.Endpoints[1], "http://test.example.com/v1/",
		fmt.Sprintf("Expected first endpoint to be http://test.example.com/v1/ but found %s instead", data.Endpoints[1]))

}

func TestPushImageJSONRegistry(t *testing.T) {
	r := spawnTestRegistrySession(t)
	imgData := &ImgData{
		ID:       "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
		Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
	}

	err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPushImageLayerRegistry(t *testing.T) {
	r := spawnTestRegistrySession(t)
	layer := strings.NewReader("")
	_, _, err := r.PushImageLayerRegistry(IMAGE_ID, layer, makeURL("/v1/"), TOKEN, []byte{})
	if err != nil {
		t.Fatal(err)
	}
}

func TestResolveRepositoryName(t *testing.T) {
	_, _, err := ResolveRepositoryName("https://github.com/docker/docker")
	assertEqual(t, err, ErrInvalidRepositoryName, "Expected error invalid repo name")
	ep, repo, err := ResolveRepositoryName("fooo/bar")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be index server address")
	assertEqual(t, repo, "fooo/bar", "Expected resolved repo to be foo/bar")

	u := makeURL("")[7:]
	ep, repo, err = ResolveRepositoryName(u + "/private/moonbase")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, ep, u, "Expected endpoint to be "+u)
	assertEqual(t, repo, "private/moonbase", "Expected endpoint to be private/moonbase")

	ep, repo, err = ResolveRepositoryName("ubuntu-12.04-base")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be "+IndexServerAddress())
	assertEqual(t, repo, "ubuntu-12.04-base", "Expected endpoint to be ubuntu-12.04-base")
}

func TestPushRegistryTag(t *testing.T) {
	r := spawnTestRegistrySession(t)
	err := r.PushRegistryTag("foo42/bar", IMAGE_ID, "stable", makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPushImageJSONIndex(t *testing.T) {
	r := spawnTestRegistrySession(t)
	imgData := []*ImgData{
		{
			ID:       "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
			Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
		},
		{
			ID:       "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
		},
	}
	repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil)
	if err != nil {
		t.Fatal(err)
	}
	if repoData == nil {
		t.Fatal("Expected RepositoryData object")
	}
	repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint})
	if err != nil {
		t.Fatal(err)
	}
	if repoData == nil {
		t.Fatal("Expected RepositoryData object")
	}
}

func TestSearchRepositories(t *testing.T) {
	r := spawnTestRegistrySession(t)
	results, err := r.SearchRepositories("fakequery")
	if err != nil {
		t.Fatal(err)
	}
	if results == nil {
		t.Fatal("Expected non-nil SearchResults object")
	}
	assertEqual(t, results.NumResults, 1, "Expected 1 search results")
	assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query")
	assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' a ot hae 42 stars")
}

func TestValidRepositoryName(t *testing.T) {
	if err := validateRepositoryName("docker/docker"); err != nil {
		t.Fatal(err)
	}
	// Support 64-byte non-hexadecimal names (hexadecimal names are forbidden)
	if err := validateRepositoryName("thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev"); err != nil {
		t.Fatal(err)
	}
	if err := validateRepositoryName("docker/Docker"); err == nil {
		t.Log("Repository name should be invalid")
		t.Fail()
	}
	if err := validateRepositoryName("docker///docker"); err == nil {
		t.Log("Repository name should be invalid")
		t.Fail()
	}
	if err := validateRepositoryName("1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a"); err == nil {
		t.Log("Repository name should be invalid, 64-byte hexadecimal names forbidden")
		t.Fail()
	}
}

func TestTrustedLocation(t *testing.T) {
	for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.io", "https://fakedocker.com"} {
		req, _ := http.NewRequest("GET", url, nil)
		if trustedLocation(req) == true {
			t.Fatalf("'%s' shouldn't be detected as a trusted location", url)
		}
	}

	for _, url := range []string{"https://docker.io", "https://test.docker.io:80"} {
		req, _ := http.NewRequest("GET", url, nil)
		if trustedLocation(req) == false {
			t.Fatalf("'%s' should be detected as a trusted location", url)
		}
	}
}

func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
	for _, urls := range [][]string{
		{"http://docker.io", "https://docker.com"},
		{"https://foo.docker.io:7777", "http://bar.docker.com"},
		{"https://foo.docker.io", "https://example.com"},
	} {
		reqFrom, _ := http.NewRequest("GET", urls[0], nil)
		reqFrom.Header.Add("Content-Type", "application/json")
		reqFrom.Header.Add("Authorization", "super_secret")
		reqTo, _ := http.NewRequest("GET", urls[1], nil)

		AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})

		if len(reqTo.Header) != 1 {
			t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
		}

		if reqTo.Header.Get("Content-Type") != "application/json" {
			t.Fatal("'Content-Type' should be 'application/json'")
		}

		if reqTo.Header.Get("Authorization") != "" {
			t.Fatal("'Authorization' should be empty")
		}
	}

	for _, urls := range [][]string{
		{"https://docker.io", "https://docker.com"},
		{"https://foo.docker.io:7777", "https://bar.docker.com"},
	} {
		reqFrom, _ := http.NewRequest("GET", urls[0], nil)
		reqFrom.Header.Add("Content-Type", "application/json")
		reqFrom.Header.Add("Authorization", "super_secret")
		reqTo, _ := http.NewRequest("GET", urls[1], nil)

		AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})

		if len(reqTo.Header) != 2 {
			t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
		}

		if reqTo.Header.Get("Content-Type") != "application/json" {
			t.Fatal("'Content-Type' should be 'application/json'")
		}

		if reqTo.Header.Get("Authorization") != "super_secret" {
			t.Fatal("'Authorization' should be 'super_secret'")
		}
	}
}
<commit_msg>Replace get.docker.io -> get.docker.com and test.docker.io -> test.docker.com<commit_after>package registry

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

	"github.com/docker/docker/utils"
)

var (
	IMAGE_ID = "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d"
	TOKEN    = []string{"fake-token"}
	REPO     = "foo42/bar"
)

func spawnTestRegistrySession(t *testing.T) *Session {
	authConfig := &AuthConfig{}
	r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), makeURL("/v1/"), true)
	if err != nil {
		t.Fatal(err)
	}
	return r
}

func TestPingRegistryEndpoint(t *testing.T) {
	regInfo, err := pingRegistryEndpoint(makeURL("/v1/"))
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, regInfo.Standalone, true, "Expected standalone to be true (default)")
}

func TestGetRemoteHistory(t *testing.T) {
	r := spawnTestRegistrySession(t)
	hist, err := r.GetRemoteHistory(IMAGE_ID, makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, len(hist), 2, "Expected 2 images in history")
	assertEqual(t, hist[0], IMAGE_ID, "Expected "+IMAGE_ID+"as first ancestry")
	assertEqual(t, hist[1], "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
		"Unexpected second ancestry")
}

func TestLookupRemoteImage(t *testing.T) {
	r := spawnTestRegistrySession(t)
	found := r.LookupRemoteImage(IMAGE_ID, makeURL("/v1/"), TOKEN)
	assertEqual(t, found, true, "Expected remote lookup to succeed")
	found = r.LookupRemoteImage("abcdef", makeURL("/v1/"), TOKEN)
	assertEqual(t, found, false, "Expected remote lookup to fail")
}

func TestGetRemoteImageJSON(t *testing.T) {
	r := spawnTestRegistrySession(t)
	json, size, err := r.GetRemoteImageJSON(IMAGE_ID, makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, size, 154, "Expected size 154")
	if len(json) <= 0 {
		t.Fatal("Expected non-empty json")
	}

	_, _, err = r.GetRemoteImageJSON("abcdef", makeURL("/v1/"), TOKEN)
	if err == nil {
		t.Fatal("Expected image not found error")
	}
}

func TestGetRemoteImageLayer(t *testing.T) {
	r := spawnTestRegistrySession(t)
	data, err := r.GetRemoteImageLayer(IMAGE_ID, makeURL("/v1/"), TOKEN, 0)
	if err != nil {
		t.Fatal(err)
	}
	if data == nil {
		t.Fatal("Expected non-nil data result")
	}

	_, err = r.GetRemoteImageLayer("abcdef", makeURL("/v1/"), TOKEN, 0)
	if err == nil {
		t.Fatal("Expected image not found error")
	}
}

func TestGetRemoteTags(t *testing.T) {
	r := spawnTestRegistrySession(t)
	tags, err := r.GetRemoteTags([]string{makeURL("/v1/")}, REPO, TOKEN)
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, len(tags), 1, "Expected one tag")
	assertEqual(t, tags["latest"], IMAGE_ID, "Expected tag latest to map to "+IMAGE_ID)

	_, err = r.GetRemoteTags([]string{makeURL("/v1/")}, "foo42/baz", TOKEN)
	if err == nil {
		t.Fatal("Expected error when fetching tags for bogus repo")
	}
}

func TestGetRepositoryData(t *testing.T) {
	r := spawnTestRegistrySession(t)
	parsedUrl, err := url.Parse(makeURL("/v1/"))
	if err != nil {
		t.Fatal(err)
	}
	host := "http://" + parsedUrl.Host + "/v1/"
	data, err := r.GetRepositoryData("foo42/bar")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, len(data.ImgList), 2, "Expected 2 images in ImgList")
	assertEqual(t, len(data.Endpoints), 2,
		fmt.Sprintf("Expected 2 endpoints in Endpoints, found %d instead", len(data.Endpoints)))
	assertEqual(t, data.Endpoints[0], host,
		fmt.Sprintf("Expected first endpoint to be %s but found %s instead", host, data.Endpoints[0]))
	assertEqual(t, data.Endpoints[1], "http://test.example.com/v1/",
		fmt.Sprintf("Expected first endpoint to be http://test.example.com/v1/ but found %s instead", data.Endpoints[1]))

}

func TestPushImageJSONRegistry(t *testing.T) {
	r := spawnTestRegistrySession(t)
	imgData := &ImgData{
		ID:       "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
		Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
	}

	err := r.PushImageJSONRegistry(imgData, []byte{0x42, 0xdf, 0x0}, makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPushImageLayerRegistry(t *testing.T) {
	r := spawnTestRegistrySession(t)
	layer := strings.NewReader("")
	_, _, err := r.PushImageLayerRegistry(IMAGE_ID, layer, makeURL("/v1/"), TOKEN, []byte{})
	if err != nil {
		t.Fatal(err)
	}
}

func TestResolveRepositoryName(t *testing.T) {
	_, _, err := ResolveRepositoryName("https://github.com/docker/docker")
	assertEqual(t, err, ErrInvalidRepositoryName, "Expected error invalid repo name")
	ep, repo, err := ResolveRepositoryName("fooo/bar")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be index server address")
	assertEqual(t, repo, "fooo/bar", "Expected resolved repo to be foo/bar")

	u := makeURL("")[7:]
	ep, repo, err = ResolveRepositoryName(u + "/private/moonbase")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, ep, u, "Expected endpoint to be "+u)
	assertEqual(t, repo, "private/moonbase", "Expected endpoint to be private/moonbase")

	ep, repo, err = ResolveRepositoryName("ubuntu-12.04-base")
	if err != nil {
		t.Fatal(err)
	}
	assertEqual(t, ep, IndexServerAddress(), "Expected endpoint to be "+IndexServerAddress())
	assertEqual(t, repo, "ubuntu-12.04-base", "Expected endpoint to be ubuntu-12.04-base")
}

func TestPushRegistryTag(t *testing.T) {
	r := spawnTestRegistrySession(t)
	err := r.PushRegistryTag("foo42/bar", IMAGE_ID, "stable", makeURL("/v1/"), TOKEN)
	if err != nil {
		t.Fatal(err)
	}
}

func TestPushImageJSONIndex(t *testing.T) {
	r := spawnTestRegistrySession(t)
	imgData := []*ImgData{
		{
			ID:       "77dbf71da1d00e3fbddc480176eac8994025630c6590d11cfc8fe1209c2a1d20",
			Checksum: "sha256:1ac330d56e05eef6d438586545ceff7550d3bdcb6b19961f12c5ba714ee1bb37",
		},
		{
			ID:       "42d718c941f5c532ac049bf0b0ab53f0062f09a03afd4aa4a02c098e46032b9d",
			Checksum: "sha256:bea7bf2e4bacd479344b737328db47b18880d09096e6674165533aa994f5e9f2",
		},
	}
	repoData, err := r.PushImageJSONIndex("foo42/bar", imgData, false, nil)
	if err != nil {
		t.Fatal(err)
	}
	if repoData == nil {
		t.Fatal("Expected RepositoryData object")
	}
	repoData, err = r.PushImageJSONIndex("foo42/bar", imgData, true, []string{r.indexEndpoint})
	if err != nil {
		t.Fatal(err)
	}
	if repoData == nil {
		t.Fatal("Expected RepositoryData object")
	}
}

func TestSearchRepositories(t *testing.T) {
	r := spawnTestRegistrySession(t)
	results, err := r.SearchRepositories("fakequery")
	if err != nil {
		t.Fatal(err)
	}
	if results == nil {
		t.Fatal("Expected non-nil SearchResults object")
	}
	assertEqual(t, results.NumResults, 1, "Expected 1 search results")
	assertEqual(t, results.Query, "fakequery", "Expected 'fakequery' as query")
	assertEqual(t, results.Results[0].StarCount, 42, "Expected 'fakeimage' a ot hae 42 stars")
}

func TestValidRepositoryName(t *testing.T) {
	if err := validateRepositoryName("docker/docker"); err != nil {
		t.Fatal(err)
	}
	// Support 64-byte non-hexadecimal names (hexadecimal names are forbidden)
	if err := validateRepositoryName("thisisthesongthatneverendsitgoesonandonandonthisisthesongthatnev"); err != nil {
		t.Fatal(err)
	}
	if err := validateRepositoryName("docker/Docker"); err == nil {
		t.Log("Repository name should be invalid")
		t.Fail()
	}
	if err := validateRepositoryName("docker///docker"); err == nil {
		t.Log("Repository name should be invalid")
		t.Fail()
	}
	if err := validateRepositoryName("1a3f5e7d9c1b3a5f7e9d1c3b5a7f9e1d3c5b7a9f1e3d5d7c9b1a3f5e7d9c1b3a"); err == nil {
		t.Log("Repository name should be invalid, 64-byte hexadecimal names forbidden")
		t.Fail()
	}
}

func TestTrustedLocation(t *testing.T) {
	for _, url := range []string{"http://example.com", "https://example.com:7777", "http://docker.io", "http://test.docker.com", "https://fakedocker.com"} {
		req, _ := http.NewRequest("GET", url, nil)
		if trustedLocation(req) == true {
			t.Fatalf("'%s' shouldn't be detected as a trusted location", url)
		}
	}

	for _, url := range []string{"https://docker.io", "https://test.docker.com:80"} {
		req, _ := http.NewRequest("GET", url, nil)
		if trustedLocation(req) == false {
			t.Fatalf("'%s' should be detected as a trusted location", url)
		}
	}
}

func TestAddRequiredHeadersToRedirectedRequests(t *testing.T) {
	for _, urls := range [][]string{
		{"http://docker.io", "https://docker.com"},
		{"https://foo.docker.io:7777", "http://bar.docker.com"},
		{"https://foo.docker.io", "https://example.com"},
	} {
		reqFrom, _ := http.NewRequest("GET", urls[0], nil)
		reqFrom.Header.Add("Content-Type", "application/json")
		reqFrom.Header.Add("Authorization", "super_secret")
		reqTo, _ := http.NewRequest("GET", urls[1], nil)

		AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})

		if len(reqTo.Header) != 1 {
			t.Fatalf("Expected 1 headers, got %d", len(reqTo.Header))
		}

		if reqTo.Header.Get("Content-Type") != "application/json" {
			t.Fatal("'Content-Type' should be 'application/json'")
		}

		if reqTo.Header.Get("Authorization") != "" {
			t.Fatal("'Authorization' should be empty")
		}
	}

	for _, urls := range [][]string{
		{"https://docker.io", "https://docker.com"},
		{"https://foo.docker.io:7777", "https://bar.docker.com"},
	} {
		reqFrom, _ := http.NewRequest("GET", urls[0], nil)
		reqFrom.Header.Add("Content-Type", "application/json")
		reqFrom.Header.Add("Authorization", "super_secret")
		reqTo, _ := http.NewRequest("GET", urls[1], nil)

		AddRequiredHeadersToRedirectedRequests(reqTo, []*http.Request{reqFrom})

		if len(reqTo.Header) != 2 {
			t.Fatalf("Expected 2 headers, got %d", len(reqTo.Header))
		}

		if reqTo.Header.Get("Content-Type") != "application/json" {
			t.Fatal("'Content-Type' should be 'application/json'")
		}

		if reqTo.Header.Get("Authorization") != "super_secret" {
			t.Fatal("'Authorization' should be 'super_secret'")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"encoding/base32"
	"github.com/ha/doozer"
	"os"
	"time"
)

const attachTimeout = 1e9

func boot(name, id, laddr, buri string) *doozer.Conn {
	b, err := doozer.DialUri(buri, "")
	if err != nil {
		panic(err)
	}

	err = b.Access(rwsk)
	if err != nil {
		panic(err)
	}

	cl := lookupAndAttach(b, name)
	if cl == nil {
		return elect(name, id, laddr, b)
	}

	return cl
}

// Elect chooses a seed node, and returns a connection to a cal.
// If this process is the seed, returns nil.
func elect(name, id, laddr string, b *doozer.Conn) *doozer.Conn {
	// advertise our presence, since we might become a cal
	nspath := "/ctl/ns/" + name + "/" + id
	r, err := b.Set(nspath, 0, []byte(laddr))
	if err != nil {
		panic(err)
	}

	// fight to be the seed
	_, err = b.Set("/ctl/boot/"+name, 0, []byte(id))
	if err, ok := err.(*doozer.Error); ok && err.Err == doozer.ErrOldRev {
		// we lost, lookup addresses again
		cl := lookupAndAttach(b, name)
		if cl == nil {
			panic("failed to attach after losing election")
		}

		// also delete our entry, since we're not officially a cal yet.
		// it gets set again in peer.Main when we become a cal.
		err := b.Del(nspath, r)
		if err != nil {
			panic(err)
		}

		return cl
	} else if err != nil {
		panic(err)
	}

	return nil // we are the seed node -- don't attach
}

func lookupAndAttach(b *doozer.Conn, name string) *doozer.Conn {
	as := lookup(b, name)
	if len(as) > 0 {
		cl := attach(name, as)
		if cl != nil {
			return cl
		}
	}
	return nil
}

func attach(name string, addrs []string) *doozer.Conn {
	ch := make(chan *doozer.Conn, 1)

	for _, a := range addrs {
		go func(a string) {
			if c, _ := isCal(name, a); c != nil {
				ch <- c
			}
		}(a)
	}

	go func() {
		<-time.After(attachTimeout)
		ch <- nil
	}()

	return <-ch
}

// IsCal checks if addr is a CAL in the cluster named name.
// Returns a client if so, nil if not.
func isCal(name, addr string) (*doozer.Conn, os.Error) {
	c, err := doozer.Dial(addr)
	if err != nil {
		return nil, err
	}

	err = c.Access(rwsk)
	if err != nil {
		return nil, err
	}

	v, _, _ := c.Get("/ctl/name", nil)
	if string(v) != name {
		return nil, nil
	}

	rev, err := c.Rev()
	if err != nil {
		return nil, err
	}

	var cals []string
	names, err := c.Getdir("/ctl/cal", rev, 0, -1)
	if err != nil {
		return nil, err
	}
	for _, name := range names {
		cals = append(cals, name)
	}

	for _, cal := range cals {
		body, _, err := c.Get("/ctl/cal/"+cal, nil)
		if err != nil || len(body) == 0 {
			continue
		}

		id := string(body)

		v, _, err := c.Get("/ctl/node/"+id+"/addr", nil)
		if err != nil {
			return nil, err
		}
		if string(v) == addr {
			return c, nil
		}
	}

	return nil, nil
}

// Find possible addresses for cluster named name.
func lookup(b *doozer.Conn, name string) (as []string) {
	rev, err := b.Rev()
	if err != nil {
		panic(err)
	}

	path := "/ctl/ns/" + name
	names, err := b.Getdir(path, rev, 0, -1)
	if err, ok := err.(*doozer.Error); ok && err.Err == doozer.ErrNoEnt {
		return nil
	} else if err != nil {
		panic(err)
	}

	path += "/"
	for _, name := range names {
		body, _, err := b.Get(path+name, &rev)
		if err != nil {
			panic(err)
		}
		as = append(as, string(body))
	}
	return as
}

func randId() string {
	const bits = 80 // enough for 10**8 ids with p(collision) < 10**-8
	rnd := make([]byte, bits/8)

	n, err := rand.Read(rnd)
	if err != nil {
		panic(err)
	}
	if n != len(rnd) {
		panic("io.ReadFull len mismatch")
	}

	enc := make([]byte, base32.StdEncoding.EncodedLen(len(rnd)))
	base32.StdEncoding.Encode(enc, rnd)
	return string(enc)
}
<commit_msg>doozerd: check for simple error code as well<commit_after>package main

import (
	"crypto/rand"
	"encoding/base32"
	"github.com/ha/doozer"
	"os"
	"time"
)

const attachTimeout = 1e9

func boot(name, id, laddr, buri string) *doozer.Conn {
	b, err := doozer.DialUri(buri, "")
	if err != nil {
		panic(err)
	}

	err = b.Access(rwsk)
	if err != nil {
		panic(err)
	}

	cl := lookupAndAttach(b, name)
	if cl == nil {
		return elect(name, id, laddr, b)
	}

	return cl
}

// Elect chooses a seed node, and returns a connection to a cal.
// If this process is the seed, returns nil.
func elect(name, id, laddr string, b *doozer.Conn) *doozer.Conn {
	// advertise our presence, since we might become a cal
	nspath := "/ctl/ns/" + name + "/" + id
	r, err := b.Set(nspath, 0, []byte(laddr))
	if err != nil {
		panic(err)
	}

	// fight to be the seed
	_, err = b.Set("/ctl/boot/"+name, 0, []byte(id))
	if err, ok := err.(*doozer.Error); ok && err.Err == doozer.ErrOldRev {
		// we lost, lookup addresses again
		cl := lookupAndAttach(b, name)
		if cl == nil {
			panic("failed to attach after losing election")
		}

		// also delete our entry, since we're not officially a cal yet.
		// it gets set again in peer.Main when we become a cal.
		err := b.Del(nspath, r)
		if err != nil {
			panic(err)
		}

		return cl
	} else if err != nil {
		panic(err)
	}

	return nil // we are the seed node -- don't attach
}

func lookupAndAttach(b *doozer.Conn, name string) *doozer.Conn {
	as := lookup(b, name)
	if len(as) > 0 {
		cl := attach(name, as)
		if cl != nil {
			return cl
		}
	}
	return nil
}

func attach(name string, addrs []string) *doozer.Conn {
	ch := make(chan *doozer.Conn, 1)

	for _, a := range addrs {
		go func(a string) {
			if c, _ := isCal(name, a); c != nil {
				ch <- c
			}
		}(a)
	}

	go func() {
		<-time.After(attachTimeout)
		ch <- nil
	}()

	return <-ch
}

// IsCal checks if addr is a CAL in the cluster named name.
// Returns a client if so, nil if not.
func isCal(name, addr string) (*doozer.Conn, os.Error) {
	c, err := doozer.Dial(addr)
	if err != nil {
		return nil, err
	}

	err = c.Access(rwsk)
	if err != nil {
		return nil, err
	}

	v, _, _ := c.Get("/ctl/name", nil)
	if string(v) != name {
		return nil, nil
	}

	rev, err := c.Rev()
	if err != nil {
		return nil, err
	}

	var cals []string
	names, err := c.Getdir("/ctl/cal", rev, 0, -1)
	if err != nil {
		return nil, err
	}
	for _, name := range names {
		cals = append(cals, name)
	}

	for _, cal := range cals {
		body, _, err := c.Get("/ctl/cal/"+cal, nil)
		if err != nil || len(body) == 0 {
			continue
		}

		id := string(body)

		v, _, err := c.Get("/ctl/node/"+id+"/addr", nil)
		if err != nil {
			return nil, err
		}
		if string(v) == addr {
			return c, nil
		}
	}

	return nil, nil
}

// Find possible addresses for cluster named name.
func lookup(b *doozer.Conn, name string) (as []string) {
	rev, err := b.Rev()
	if err != nil {
		panic(err)
	}

	path := "/ctl/ns/" + name
	names, err := b.Getdir(path, rev, 0, -1)
	if err == doozer.ErrNoEnt {
		return nil
	} else if err, ok := err.(*doozer.Error); ok && err.Err == doozer.ErrNoEnt {
		return nil
	} else if err != nil {
		panic(err)
	}

	path += "/"
	for _, name := range names {
		body, _, err := b.Get(path+name, &rev)
		if err != nil {
			panic(err)
		}
		as = append(as, string(body))
	}
	return as
}

func randId() string {
	const bits = 80 // enough for 10**8 ids with p(collision) < 10**-8
	rnd := make([]byte, bits/8)

	n, err := rand.Read(rnd)
	if err != nil {
		panic(err)
	}
	if n != len(rnd) {
		panic("io.ReadFull len mismatch")
	}

	enc := make([]byte, base32.StdEncoding.EncodedLen(len(rnd)))
	base32.StdEncoding.Encode(enc, rnd)
	return string(enc)
}
<|endoftext|>


<commit_before>package gosum

import (
	"bytes"
	"io"
	"testing"
)

func TestAdd(t *testing.T) {
	md5sum := NewMD5Sum()
	md5sum.Add("a.txt", bytes.NewReader([]byte("test")))
	expected := "098f6bcd4621d373cade4e832627b4f6  a.txt\n"

	var result bytes.Buffer
	io.Copy(&result, md5sum)

	if result.String() != expected {
		t.Error("md5sum.Add checksum mismatch")
	}
}
<commit_msg>Add a test for md5sum Check<commit_after>package gosum

import (
	"bytes"
	"io"
	"testing"
)

func TestAdd(t *testing.T) {
	md5sum := NewMD5Sum()
	md5sum.Add("a.txt", bytes.NewReader([]byte("test")))
	expected := "098f6bcd4621d373cade4e832627b4f6  a.txt\n"

	var result bytes.Buffer
	io.Copy(&result, md5sum)

	if result.String() != expected {
		t.Error("md5sum.Add checksum mismatch")
	}
}

func TestCheck(t *testing.T) {
	md5sum := NewMD5Sum()
	md5sum.Add("a.txt", bytes.NewReader([]byte("test")))

	ok, err := md5sum.Check("a.txt", bytes.NewReader([]byte("test")))
	if err != nil {
		t.Error(err)
	}
	if !ok {
		t.Error("md5sum.Add checksum mismatch")
	}
}
<|endoftext|>


<commit_before><commit_msg>initialized<commit_after><|endoftext|>


<commit_before>package sockjsclient

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
)

// the implementation of New() doesn't have any error to be returned yet it
// returns, so it's totally safe to neglect the error
var cookieJar, _ = cookiejar.New(nil)

type XHRSession struct {
	client     *http.Client
	sessionURL string
	sessionID  string
	messages   []string
	opened     bool
}

// NewXHRSession returns a new XHRSession, a SockJS client which supports
// xhr-polling
// http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-74
func NewXHRSession(opts *DialOptions) (*XHRSession, error) {
	client := &http.Client{
		// never make it less than the heartbeat delay from the sockjs server.
		// If this is los, your requests to the server will time out, so you'll
		// never receive the heartbeat frames.
		Timeout: opts.Timeout,
		// add this so we can make use of load balancer's sticky session features,
		// such as AWS ELB
		Jar: cookieJar,
	}

	// following /server_id/session_id should always be the same for every session
	serverID := threeDigits()
	sessionID := randomStringLength(20)
	sessionURL := opts.BaseURL + "/" + serverID + "/" + sessionID

	// start the initial session handshake
	sessionResp, err := client.Post(sessionURL+"/xhr", "text/plain", nil)
	if err != nil {
		return nil, err
	}
	defer sessionResp.Body.Close()

	buf := bufio.NewReader(sessionResp.Body)
	frame, err := buf.ReadByte()
	if err != nil {
		return nil, err
	}

	if frame != 'o' {
		return nil, fmt.Errorf("can't start session, invalid frame: %s", frame)
	}

	return &XHRSession{
		client:     client,
		sessionID:  sessionID,
		sessionURL: sessionURL,
		opened:     true,
	}, nil
}

func (x *XHRSession) ID() string {
	return x.sessionID
}

func (x *XHRSession) Recv() (string, error) {
	// Return previously received messages if there is any.
	if len(x.messages) > 0 {
		msg := x.messages[0]
		x.messages = x.messages[1:]
		return msg, nil
	}

	// start to poll from the server until we receive something
	for {
		resp, err := x.client.Post(x.sessionURL+"/xhr", "text/plain", nil)
		if err != nil {
			return "", err
		}
		defer resp.Body.Close()

		buf := bufio.NewReader(resp.Body)

		// returns an error if buffer is empty
		frame, err := buf.ReadByte()
		if err != nil {
			return "", err
		}

		switch frame {
		case 'o':
			// session started
			x.opened = true
			continue
		case 'a':
			// received an array of messages
			data, err := ioutil.ReadAll(buf)
			if err != nil {
				return "", err
			}

			var messages []string
			err = json.Unmarshal(data, &messages)
			if err != nil {
				return "", err
			}

			x.messages = append(x.messages, messages...)

			if len(x.messages) == 0 {
				return "", errors.New("no message")
			}

			// Return first message in slice, and remove it from the slice, so
			// next time the others will be picked
			msg := x.messages[0]
			x.messages = x.messages[1:]

			return msg, nil
		case 'h':
			// heartbeat received
			continue
		case 'c':
			x.opened = false
			return "", errors.New("session closed")
		default:
			return "", errors.New("invalid frame type")
		}
	}

	return "", errors.New("FATAL: If we get here, please revisit the logic again")
}

func (x *XHRSession) Send(frame string) error {
	if !x.opened {
		return errors.New("session is not opened yet")
	}

	// Need's to be JSON encoded array of string messages (SockJS protocol
	// requirement)
	message := []string{frame}
	body, err := json.Marshal(&message)
	if err != nil {
		return err
	}

	resp, err := x.client.Post(x.sessionURL+"/xhr_send", "text/plain", bytes.NewReader(body))
	if err != nil {
		return err
	}

	if resp.StatusCode == 404 {
		return errors.New("XHR session doesn't exists")
	}

	if resp.StatusCode != 204 {
		return fmt.Errorf("Sending failed: %d", resp.StatusCode)
	}

	return nil
}

func (x *XHRSession) Close(status uint32, reason string) error {
	return nil
}
<commit_msg>sockjsclient: protect state field<commit_after>package sockjsclient

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/cookiejar"
	"sync"
)

// the implementation of New() doesn't have any error to be returned yet it
// returns, so it's totally safe to neglect the error
var cookieJar, _ = cookiejar.New(nil)

type XHRSession struct {
	mu sync.Mutex

	client     *http.Client
	sessionURL string
	sessionID  string
	messages   []string
	opened     bool
}

// NewXHRSession returns a new XHRSession, a SockJS client which supports
// xhr-polling
// http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-74
func NewXHRSession(opts *DialOptions) (*XHRSession, error) {
	client := &http.Client{
		// never make it less than the heartbeat delay from the sockjs server.
		// If this is los, your requests to the server will time out, so you'll
		// never receive the heartbeat frames.
		Timeout: opts.Timeout,
		// add this so we can make use of load balancer's sticky session features,
		// such as AWS ELB
		Jar: cookieJar,
	}

	// following /server_id/session_id should always be the same for every session
	serverID := threeDigits()
	sessionID := randomStringLength(20)
	sessionURL := opts.BaseURL + "/" + serverID + "/" + sessionID

	// start the initial session handshake
	sessionResp, err := client.Post(sessionURL+"/xhr", "text/plain", nil)
	if err != nil {
		return nil, err
	}
	defer sessionResp.Body.Close()

	buf := bufio.NewReader(sessionResp.Body)
	frame, err := buf.ReadByte()
	if err != nil {
		return nil, err
	}

	if frame != 'o' {
		return nil, fmt.Errorf("can't start session, invalid frame: %s", frame)
	}

	return &XHRSession{
		client:     client,
		sessionID:  sessionID,
		sessionURL: sessionURL,
		opened:     true,
	}, nil
}

func (x *XHRSession) ID() string {
	return x.sessionID
}

func (x *XHRSession) Recv() (string, error) {
	// Return previously received messages if there is any.
	if len(x.messages) > 0 {
		msg := x.messages[0]
		x.messages = x.messages[1:]
		return msg, nil
	}

	// start to poll from the server until we receive something
	for {
		resp, err := x.client.Post(x.sessionURL+"/xhr", "text/plain", nil)
		if err != nil {
			return "", err
		}
		defer resp.Body.Close()

		buf := bufio.NewReader(resp.Body)

		// returns an error if buffer is empty
		frame, err := buf.ReadByte()
		if err != nil {
			return "", err
		}

		switch frame {
		case 'o':
			// session started
			x.mu.Lock()
			x.opened = true
			x.mu.Unlock()
			continue
		case 'a':
			// received an array of messages
			data, err := ioutil.ReadAll(buf)
			if err != nil {
				return "", err
			}

			var messages []string
			err = json.Unmarshal(data, &messages)
			if err != nil {
				return "", err
			}

			x.messages = append(x.messages, messages...)

			if len(x.messages) == 0 {
				return "", errors.New("no message")
			}

			// Return first message in slice, and remove it from the slice, so
			// next time the others will be picked
			msg := x.messages[0]
			x.messages = x.messages[1:]

			return msg, nil
		case 'h':
			// heartbeat received
			continue
		case 'c':
			x.mu.Lock()
			x.opened = false
			x.mu.Unlock()
			return "", errors.New("session closed")
		default:
			return "", errors.New("invalid frame type")
		}
	}

	return "", errors.New("FATAL: If we get here, please revisit the logic again")
}

func (x *XHRSession) Send(frame string) error {
	x.mu.Lock()
	if !x.opened {
		x.mu.Unlock()
		return errors.New("session is not opened yet")
	}
	x.mu.Unlock()

	// Need's to be JSON encoded array of string messages (SockJS protocol
	// requirement)
	message := []string{frame}
	body, err := json.Marshal(&message)
	if err != nil {
		return err
	}

	resp, err := x.client.Post(x.sessionURL+"/xhr_send", "text/plain", bytes.NewReader(body))
	if err != nil {
		return err
	}

	if resp.StatusCode == 404 {
		return errors.New("XHR session doesn't exists")
	}

	if resp.StatusCode != 204 {
		return fmt.Errorf("Sending failed: %d", resp.StatusCode)
	}

	return nil
}

func (x *XHRSession) Close(status uint32, reason string) error {
	return nil
}
<|endoftext|>


<commit_before>// Package requestcache provides functions for caching on-demand generated data.
package requestcache

import (
	"bytes"
	"context"
	"encoding/gob"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sync"

	"github.com/golang/groupcache/lru"
)

// Cache is a holder for one or multiple caches.
type Cache struct {
	// requestChan receives incoming requests.
	requestChan chan *Request

	// requests holds the number of requests each individual cache has received.
	requests    []int
	requestLock sync.RWMutex
}

// NewCache creates a new set of caches for on-demand generated content, where
// processor is the function that creates the content, numProcessors is the
// number of processors that will be working in parallel, and cachefuncs are the
// caches to be used, listed in order of priority.
func NewCache(processor ProcessFunc, numProcessors int, cachefuncs ...CacheFunc) *Cache {
	c := &Cache{
		requestChan: make(chan *Request),
		requests:    make([]int, len(cachefuncs)+1),
	}

	in := c.requestChan
	out := in
	for i, cf := range cachefuncs {

		intermediate := make(chan *Request)
		go func(in chan *Request, i int) {
			// Track the number of requests received by this cache.
			for req := range in {
				c.requestLock.Lock()
				c.requests[i]++
				c.requestLock.Unlock()
				intermediate <- req
			}
		}(in, i)

		out = cf(intermediate)
		in = out
	}
	for i := 0; i < numProcessors; i++ {
		go func() {
			for req := range out {
				// Process the results
				c.requestLock.Lock()
				c.requests[len(cachefuncs)]++
				c.requestLock.Unlock()
				req.resultPayload, req.err = processor(req.ctx, req.requestPayload)
				req.returnChan <- req
			}
		}()
	}

	return c
}

// Requests returns the number of requests that each cache has received. The
// last index in the output is the number of requests received by the processor.
// So, for example, the miss rate for the first cache in c is r[len(r)-1] / r[0],
// where r is the result of this function.
func (c *Cache) Requests() []int {
	c.requestLock.Lock()
	out := make([]int, len(c.requests))
	copy(out, c.requests)
	defer c.requestLock.Unlock()
	return out
}

// ProcessFunc defines the format of functions that can be used to process
// a request payload and return a resultPayload.
type ProcessFunc func(ctx context.Context, requestPayload interface{}) (resultPayload interface{}, err error)

// Request holds information about a request that is to be handled either by
// a cache or a ProcessFunc.
type Request struct {
	ctx            context.Context
	requestPayload interface{}
	resultPayload  interface{}
	requestChan    chan *Request
	returnChan     chan *Request
	err            error
	funcs          []func(*Request)
	key            string
}

// NewRequest creates a new request where requestPayload is the input data
// that will be used to generate the results and key is a unique key.
func (c *Cache) NewRequest(ctx context.Context, requestPayload interface{}, key string) *Request {
	return &Request{
		requestPayload: requestPayload,
		returnChan:     make(chan *Request),
		requestChan:    c.requestChan,
		key:            key,
		ctx:            ctx,
	}
}

// Result sends the request for processing, waits for the result, and returns
// the result and any errors that occurred while
// processing.
func (r *Request) Result() (interface{}, error) {
	r.requestChan <- r
	rr := <-r.returnChan
	return rr.resultPayload, rr.finalize()
}

// finalize runs any clean-up functions that need to be run after the results
// have been generated and returns whether any errors have occurred.
func (r *Request) finalize() error {
	if r.err != nil {
		return r.err
	}
	for len(r.funcs) > 0 {
		f := r.funcs[0]
		r.funcs = r.funcs[1:len(r.funcs)]
		f(r)
		if r.err != nil {
			return r.err
		}
	}
	return r.err
}

// A CacheFunc can be used to store request results in a cache.
type CacheFunc func(in chan *Request) (out chan *Request)

// Deduplicate avoids duplicating requests.
func Deduplicate() CacheFunc {
	return func(in chan *Request) chan *Request {
		out := make(chan *Request)
		var dupLock sync.Mutex
		runningTasks := make(map[string][]*Request)

		dupFunc := func(req *Request) {
			dupLock.Lock()
			reqs := runningTasks[req.key]
			for i := 1; i < len(reqs); i++ {
				reqs[i].returnChan <- req
			}
			delete(runningTasks, req.key)
			dupLock.Unlock()
		}

		go func() {
			for req := range in {
				dupLock.Lock()
				if _, ok := runningTasks[req.key]; ok {
					// This task is currently running, so add it to the queue.
					runningTasks[req.key] = append(runningTasks[req.key], req)
					dupLock.Unlock()
				} else {
					// This task is not currently running, so add it to the beginning of the
					// queue and pass it on.
					runningTasks[req.key] = []*Request{req}
					req.funcs = append(req.funcs, dupFunc)
					dupLock.Unlock()
					out <- req
				}
			}
		}()
		return out
	}
}

// Memory manages an in-memory cache of results, where maxEntries is the
// max number of items in the cache. If the results returned by this cache
// are modified by the caller, they may also be modified in the cache.
func Memory(maxEntries int) CacheFunc {
	return func(in chan *Request) chan *Request {
		out := make(chan *Request)
		cache := lru.New(maxEntries)
		var mx sync.RWMutex

		// cacheFunc adds the data to the cache and is sent along
		// with the request if the data is not in the cache
		cacheFunc := func(req *Request) {
			mx.Lock()
			defer mx.Unlock()
			cache.Add(req.key, req.resultPayload)
		}

		go func() {
			for req := range in {
				mx.RLock()
				d, ok := cache.Get(req.key)
				mx.RUnlock()
				if ok {
					// If the item is in the cache, return it
					req.resultPayload = d
					req.returnChan <- req
				} else {
					// otherwise, add the request to the cache and send the request along.
					req.funcs = append(req.funcs, cacheFunc)
					out <- req
				}
			}
		}()
		return out
	}
}

// MarshalGob marshals an interface to a byte array and fulfills
// the requirements for the Disk cache marshalFunc input.
func MarshalGob(data interface{}) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	e := gob.NewEncoder(w)
	if err := e.Encode(data); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}

// UnmarshalGob unmarshals an interface from a byte array and fulfills
// the requirements for the Disk cache unmarshalFunc input.
func UnmarshalGob(b []byte) (interface{}, error) {
	r := bytes.NewBuffer(b)
	d := gob.NewDecoder(r)
	var data interface{}
	if err := d.Decode(&data); err != nil {
		return nil, err
	}
	return data, nil
}

// FileExtension is appended to request key names to make
// up the names of files being written to disk.
var FileExtension = ".dat"

// Disk manages an on-disk cache of results, where dir is the
// directory in which to store results, marshalFunc is the function to
// be used to marshal the data object to binary form, and unmarshalFunc
// is the function to be used to unmarshal the data from binary form.
func Disk(dir string, marshalFunc func(interface{}) ([]byte, error), unmarshalFunc func([]byte) (interface{}, error)) CacheFunc {
	return func(in chan *Request) chan *Request {

		out := make(chan *Request)

		// This function writes the data to the disk after it is
		// created, and is sent along with the request if the data is
		// not in the cache.
		writeFunc := func(req *Request) {
			fname := filepath.Join(dir, req.key+FileExtension)
			w, err := os.Create(fname)
			if err != nil {
				req.err = err
				return
			}
			defer w.Close()
			b, err := marshalFunc(&req.resultPayload)
			if err != nil {
				req.err = err
				return
			}
			if _, err = w.Write(b); err != nil {
				req.err = err
				return
			}
		}

		go func() {
			for req := range in {
				fname := filepath.Join(dir, req.key+FileExtension)

				f, err := os.Open(fname)
				if err != nil {
					// If we can't open the file, assume that it doesn't exist and Pass
					// the request on.
					req.funcs = append(req.funcs, writeFunc)
					out <- req
					continue
				}
				b, err := ioutil.ReadAll(f)
				if err != nil {
					// We can't read the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				data, err := unmarshalFunc(b)
				if err != nil {
					// There is some problem with the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				if err := f.Close(); err != nil {
					req.err = err
					req.returnChan <- req
					continue
				}
				// Successfully retrieved the result. Now add it to the request
				// so it is stored in the cache and return it to the requester.
				req.resultPayload = data
				req.returnChan <- req
			}
		}()
		return out
	}
}

// HTTP retrieves cached requests over an HTTP connection, where addr is the
// address where results are stored and unmarshalFunc
// is the function to be used to unmarshal the data from binary form.
// This function does not cache requests, it only retrieves previously cached
// requests.
func HTTP(addr string, unmarshalFunc func([]byte) (interface{}, error)) CacheFunc {
	return func(in chan *Request) chan *Request {

		out := make(chan *Request)

		go func() {
			for req := range in {
				fname := addr + "/" + req.key + FileExtension

				response, err := http.Get(fname)
				if err != nil {
					// If we don't get a response from the server, return an error.
					req.err = fmt.Errorf(response.Status)
					req.returnChan <- req
					continue
				}

				if response.StatusCode != 200 { // Check if the status is 'ok'.
					if response.StatusCode == 404 {
						// If we get a "not found" error, pass the request on.
						out <- req
						continue
					} else {
						// If we get a different status, return an error.
						req.err = fmt.Errorf(response.Status)
						req.returnChan <- req
						continue
					}
				}
				b, err := ioutil.ReadAll(response.Body)
				if err != nil {
					// We can't read the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				data, err := unmarshalFunc(b)
				if err != nil {
					// There is some problem with the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				if err := response.Body.Close(); err != nil {
					req.err = err
					req.returnChan <- req
					continue
				}
				// Successfully retrieved the result. Now add it to the request
				// so it is stored in the cache and return it to the requester.
				req.resultPayload = data
				req.returnChan <- req
			}
		}()
		return out
	}
}
<commit_msg>Add Google Cloud Storage cache<commit_after>// Package requestcache provides functions for caching on-demand generated data.
package requestcache

import (
	"bytes"
	"context"
	"encoding/gob"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"sync"

	"cloud.google.com/go/storage"
	"github.com/golang/groupcache/lru"
)

// Cache is a holder for one or multiple caches.
type Cache struct {
	// requestChan receives incoming requests.
	requestChan chan *Request

	// requests holds the number of requests each individual cache has received.
	requests    []int
	requestLock sync.RWMutex
}

// NewCache creates a new set of caches for on-demand generated content, where
// processor is the function that creates the content, numProcessors is the
// number of processors that will be working in parallel, and cachefuncs are the
// caches to be used, listed in order of priority.
func NewCache(processor ProcessFunc, numProcessors int, cachefuncs ...CacheFunc) *Cache {
	c := &Cache{
		requestChan: make(chan *Request),
		requests:    make([]int, len(cachefuncs)+1),
	}

	in := c.requestChan
	out := in
	for i, cf := range cachefuncs {

		intermediate := make(chan *Request)
		go func(in chan *Request, i int) {
			// Track the number of requests received by this cache.
			for req := range in {
				c.requestLock.Lock()
				c.requests[i]++
				c.requestLock.Unlock()
				intermediate <- req
			}
		}(in, i)

		out = cf(intermediate)
		in = out
	}
	for i := 0; i < numProcessors; i++ {
		go func() {
			for req := range out {
				// Process the results
				c.requestLock.Lock()
				c.requests[len(cachefuncs)]++
				c.requestLock.Unlock()
				req.resultPayload, req.err = processor(req.ctx, req.requestPayload)
				req.returnChan <- req
			}
		}()
	}

	return c
}

// Requests returns the number of requests that each cache has received. The
// last index in the output is the number of requests received by the processor.
// So, for example, the miss rate for the first cache in c is r[len(r)-1] / r[0],
// where r is the result of this function.
func (c *Cache) Requests() []int {
	c.requestLock.Lock()
	out := make([]int, len(c.requests))
	copy(out, c.requests)
	defer c.requestLock.Unlock()
	return out
}

// ProcessFunc defines the format of functions that can be used to process
// a request payload and return a resultPayload.
type ProcessFunc func(ctx context.Context, requestPayload interface{}) (resultPayload interface{}, err error)

// Request holds information about a request that is to be handled either by
// a cache or a ProcessFunc.
type Request struct {
	ctx            context.Context
	requestPayload interface{}
	resultPayload  interface{}
	requestChan    chan *Request
	returnChan     chan *Request
	err            error
	funcs          []func(*Request)
	key            string
}

// NewRequest creates a new request where requestPayload is the input data
// that will be used to generate the results and key is a unique key.
func (c *Cache) NewRequest(ctx context.Context, requestPayload interface{}, key string) *Request {
	return &Request{
		requestPayload: requestPayload,
		returnChan:     make(chan *Request),
		requestChan:    c.requestChan,
		key:            key,
		ctx:            ctx,
	}
}

// Result sends the request for processing, waits for the result, and returns
// the result and any errors that occurred while
// processing.
func (r *Request) Result() (interface{}, error) {
	r.requestChan <- r
	rr := <-r.returnChan
	return rr.resultPayload, rr.finalize()
}

// finalize runs any clean-up functions that need to be run after the results
// have been generated and returns whether any errors have occurred.
func (r *Request) finalize() error {
	if r.err != nil {
		return r.err
	}
	for len(r.funcs) > 0 {
		f := r.funcs[0]
		r.funcs = r.funcs[1:len(r.funcs)]
		f(r)
		if r.err != nil {
			return r.err
		}
	}
	return r.err
}

// A CacheFunc can be used to store request results in a cache.
type CacheFunc func(in chan *Request) (out chan *Request)

// Deduplicate avoids duplicating requests.
func Deduplicate() CacheFunc {
	return func(in chan *Request) chan *Request {
		out := make(chan *Request)
		var dupLock sync.Mutex
		runningTasks := make(map[string][]*Request)

		dupFunc := func(req *Request) {
			dupLock.Lock()
			reqs := runningTasks[req.key]
			for i := 1; i < len(reqs); i++ {
				reqs[i].returnChan <- req
			}
			delete(runningTasks, req.key)
			dupLock.Unlock()
		}

		go func() {
			for req := range in {
				dupLock.Lock()
				if _, ok := runningTasks[req.key]; ok {
					// This task is currently running, so add it to the queue.
					runningTasks[req.key] = append(runningTasks[req.key], req)
					dupLock.Unlock()
				} else {
					// This task is not currently running, so add it to the beginning of the
					// queue and pass it on.
					runningTasks[req.key] = []*Request{req}
					req.funcs = append(req.funcs, dupFunc)
					dupLock.Unlock()
					out <- req
				}
			}
		}()
		return out
	}
}

// Memory manages an in-memory cache of results, where maxEntries is the
// max number of items in the cache. If the results returned by this cache
// are modified by the caller, they may also be modified in the cache.
func Memory(maxEntries int) CacheFunc {
	return func(in chan *Request) chan *Request {
		out := make(chan *Request)
		cache := lru.New(maxEntries)
		var mx sync.RWMutex

		// cacheFunc adds the data to the cache and is sent along
		// with the request if the data is not in the cache
		cacheFunc := func(req *Request) {
			mx.Lock()
			defer mx.Unlock()
			cache.Add(req.key, req.resultPayload)
		}

		go func() {
			for req := range in {
				mx.RLock()
				d, ok := cache.Get(req.key)
				mx.RUnlock()
				if ok {
					// If the item is in the cache, return it
					req.resultPayload = d
					req.returnChan <- req
				} else {
					// otherwise, add the request to the cache and send the request along.
					req.funcs = append(req.funcs, cacheFunc)
					out <- req
				}
			}
		}()
		return out
	}
}

// MarshalGob marshals an interface to a byte array and fulfills
// the requirements for the Disk cache marshalFunc input.
func MarshalGob(data interface{}) ([]byte, error) {
	w := bytes.NewBuffer(nil)
	e := gob.NewEncoder(w)
	if err := e.Encode(data); err != nil {
		return nil, err
	}
	return w.Bytes(), nil
}

// UnmarshalGob unmarshals an interface from a byte array and fulfills
// the requirements for the Disk cache unmarshalFunc input.
func UnmarshalGob(b []byte) (interface{}, error) {
	r := bytes.NewBuffer(b)
	d := gob.NewDecoder(r)
	var data interface{}
	if err := d.Decode(&data); err != nil {
		return nil, err
	}
	return data, nil
}

// FileExtension is appended to request key names to make
// up the names of files being written to disk.
var FileExtension = ".dat"

// Disk manages an on-disk cache of results, where dir is the
// directory in which to store results, marshalFunc is the function to
// be used to marshal the data object to binary form, and unmarshalFunc
// is the function to be used to unmarshal the data from binary form.
func Disk(dir string, marshalFunc func(interface{}) ([]byte, error), unmarshalFunc func([]byte) (interface{}, error)) CacheFunc {
	return func(in chan *Request) chan *Request {

		out := make(chan *Request)

		// This function writes the data to the disk after it is
		// created, and is sent along with the request if the data is
		// not in the cache.
		writeFunc := func(req *Request) {
			fname := filepath.Join(dir, req.key+FileExtension)
			w, err := os.Create(fname)
			if err != nil {
				req.err = err
				return
			}
			defer w.Close()
			b, err := marshalFunc(&req.resultPayload)
			if err != nil {
				req.err = err
				return
			}
			if _, err = w.Write(b); err != nil {
				req.err = err
				return
			}
		}

		go func() {
			for req := range in {
				fname := filepath.Join(dir, req.key+FileExtension)

				f, err := os.Open(fname)
				if err != nil {
					// If we can't open the file, assume that it doesn't exist and Pass
					// the request on.
					req.funcs = append(req.funcs, writeFunc)
					out <- req
					continue
				}
				b, err := ioutil.ReadAll(f)
				if err != nil {
					// We can't read the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				data, err := unmarshalFunc(b)
				if err != nil {
					// There is some problem with the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				if err := f.Close(); err != nil {
					req.err = err
					req.returnChan <- req
					continue
				}
				// Successfully retrieved the result. Now add it to the request
				// so it is stored in the cache and return it to the requester.
				req.resultPayload = data
				req.returnChan <- req
			}
		}()
		return out
	}
}

// HTTP retrieves cached requests over an HTTP connection, where addr is the
// address where results are stored and unmarshalFunc
// is the function to be used to unmarshal the data from binary form.
// This function does not cache requests, it only retrieves previously cached
// requests.
func HTTP(addr string, unmarshalFunc func([]byte) (interface{}, error)) CacheFunc {
	return func(in chan *Request) chan *Request {

		out := make(chan *Request)

		go func() {
			for req := range in {
				fname := addr + "/" + req.key + FileExtension

				response, err := http.Get(fname)
				if err != nil {
					// If we don't get a response from the server, return an error.
					req.err = fmt.Errorf(response.Status)
					req.returnChan <- req
					continue
				}

				if response.StatusCode != 200 { // Check if the status is 'ok'.
					if response.StatusCode == 404 {
						// If we get a "not found" error, pass the request on.
						out <- req
						continue
					} else {
						// If we get a different status, return an error.
						req.err = fmt.Errorf(response.Status)
						req.returnChan <- req
						continue
					}
				}
				b, err := ioutil.ReadAll(response.Body)
				if err != nil {
					// We can't read the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				data, err := unmarshalFunc(b)
				if err != nil {
					// There is some problem with the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				if err := response.Body.Close(); err != nil {
					req.err = err
					req.returnChan <- req
					continue
				}
				// Successfully retrieved the result. Now add it to the request
				// so it is stored in the cache and return it to the requester.
				req.resultPayload = data
				req.returnChan <- req
			}
		}()
		return out
	}
}

// GoogleCloudStorage manages an cache of results in Google Cloud Storage,
// where bucket is the bucket in which to store results, subdir is the
// bucket subdirectory, if any, that should be used, marshalFunc is
// the function to
// be used to marshal the data object to binary form, and unmarshalFunc
// is the function to be used to unmarshal the data from binary form.
func GoogleCloudStorage(ctx context.Context, bucket, subdir string, marshalFunc func(interface{}) ([]byte, error), unmarshalFunc func([]byte) (interface{}, error)) (CacheFunc, error) {
	client, err := storage.NewClient(ctx)
	if err != nil {
		return nil, err
	}
	bkt := client.Bucket(bucket)
	return func(in chan *Request) chan *Request {
		out := make(chan *Request)

		// This function writes the data to the disk after it is
		// created, and is sent along with the request if the data is
		// not in the cache.
		writeFunc := func(req *Request) {
			obj := bkt.Object(subdir + "/" + req.key + FileExtension)
			w := obj.NewWriter(req.ctx)
			defer w.Close()
			b, err := marshalFunc(&req.resultPayload)
			if err != nil {
				req.err = err
				return
			}
			if _, err = w.Write(b); err != nil {
				req.err = err
				return
			}
		}

		go func() {
			for req := range in {
				obj := bkt.Object(subdir + "/" + req.key + FileExtension)
				f, err := obj.NewReader(req.ctx)
				if err != nil {
					if err == storage.ErrObjectNotExist {
						req.funcs = append(req.funcs, writeFunc)
						out <- req
						continue
					}
					req.err = err
					req.returnChan <- req
					continue
				}
				b, err := ioutil.ReadAll(f)
				if err != nil {
					// We can't read the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				data, err := unmarshalFunc(b)
				if err != nil {
					// There is some problem with the file.
					req.err = err
					req.returnChan <- req
					continue
				}
				if err := f.Close(); err != nil {
					req.err = err
					req.returnChan <- req
					continue
				}
				// Successfully retrieved the result. Now add it to the request
				// so it is stored in the cache and return it to the requester.
				req.resultPayload = data
				req.returnChan <- req
			}
		}()
		return out
	}, nil
}
<|endoftext|>


<commit_before>package readline

import (
	"errors"
	"fmt"
	"strings"
)

// TODO: print with a pretty format
func printCandidates(inst *Instance, cp Completer, candidates []string) {
	args := strings.Fields(string(inst.line))
	prefix := args[len(args)-1]
	inst.Print("\n")
	for _, n := range candidates {
		inst.Printf("%s\t", cp.modifyCandidate(prefix, n))
	}
	inst.Print("\n")
}

// @cp is the parent cmd node and must be cmd-completer.
// find the sub-completer whose name is @cmd
func findSubCompleter(inst *Instance, cmd string, cp Completer) Completer {
	if cp == nil {
		panic("Completer is nil")
	}

	inst.Log("findSubCompleter: find [%s] in %v\n", cmd,
		func(subs []Completer) []string {
			names := make([]string, 0)
			for _, c := range subs {
				names = append(names, c.name())
			}
			return names
		}(cp.subs()))

	if cp.isSp() {
		panic("Sp Completer must not have sub completers")
	}

	subs := cp.subs()
	for _, c := range subs {
		if !c.isSp() && c.name() == cmd {
			return c
		}
	}

	return nil
}

// search @cp's sub-completers and collect candidates
func getCandidatesFromSubs(inst *Instance, cp Completer) ([]string, bool, error) {
	if cp == nil {
		return nil, false, errors.New("Completer is nil")
	}

	var sp Completer
	var candidates []string
	subs := cp.subs()
	for _, c := range subs {
		if c.isSp() {
			sp = c
			break
		} else {
			candidates = append(candidates, c.name())
		}
	}

	if sp != nil {
		cans, end := sp.getCandidates(string(inst.line))
		return cans, end, nil
	}

	return candidates, true, nil
}

// find all commands in @cmds which have prefix of @arg
func getCandidatesByPrefix(inst *Instance, arg string, cp Completer) (Completer, []string, bool, error) {
	var candidates []string
	var sp Completer
	end := true
	currentCp := cp
	cps := cp.subs()
	for _, c := range cps {
		if c.isSp() {
			sp = c
		} else {
			if strings.HasPrefix(c.name(), arg) {
				candidates = append(candidates, c.name())
			}
		}
	}

	if len(candidates) == 0 && sp != nil {
		cans, _end := sp.getCandidates(string(inst.line))
		inst.Log("prefix: %s, end: %v, candidates: [%v]\n", arg, _end, cans)
		for _, v := range cans {
			if strings.HasPrefix(v, arg) {
				candidates = append(candidates, v)
			}
		}
		if len(candidates) == 1 {
			_end = true
		}
		end = _end
		currentCp = sp
	}

	return currentCp, candidates, end, nil
}

// check the inst.line and find the candidates
func getCandidates(inst *Instance) (Completer, []string, bool, error) {
	args := strings.Fields(string(inst.line))
	count := len(inst.line)
	inst.Log("args = %v, len(line) = %d\n", args, count)

	cp := inst.root
	candidates, end, err := getCandidatesFromSubs(inst, cp)
	if err != nil {
		return nil, nil, end, err
	}

	for i, arg := range args {

		lastArg := i == len(args)-1
		partialArg := inst.line[count-1] != ' '
		inst.Log("process cmd [%s], lastArg: %v, partialArg: %v\n",
			arg, lastArg, partialArg)

		if lastArg && partialArg {
			cp, candidates, end, err = getCandidatesByPrefix(inst, arg, cp)
			if err != nil {
				return nil, nil, end, err
			}
		} else {
			cp = findSubCompleter(inst, arg, cp)
			if cp == nil {
				inst.Log("can not find %s", arg)
				return nil, nil, false, errors.New(fmt.Sprintf("can not find %s", arg))
			}
			if lastArg {
				candidates, end, err = getCandidatesFromSubs(inst, cp)
				if err != nil {
					return nil, nil, end, err
				}
			}
		}
	}
	inst.Log("candidates: %v\n", candidates)
	return cp, candidates, end, nil
}

func doComplete(inst *Instance, candidate string) {
	args := strings.Fields(string(inst.line))
	count := len(args)
	if count == 0 {
		panic("when do complete, the input can not be empty fields")
	}
	todo := args[len(args)-1]
	index := strings.Index(candidate, todo) + len(todo)
	for i := index; i < len(candidate); i++ {
		inst.lineAdd(byte(candidate[i]))
	}
}

// [ls]: ls -> [ls ]
// return: whether is the auto-complete end
func completeWhole(inst *Instance, candidate string) bool {
	doComplete(inst, candidate)
	return true
}

// if all candidates have the same prefix, complete the common part
// if so, return true
// e.g. [clean], [clone]: c -> [cl]
func completePartial(inst *Instance, candidates []string) {
	prefix := lcp(candidates)
	if len(prefix) != 0 {
		doComplete(inst, prefix)
	}
}

// Via: https://rosettacode.org/wiki/Longest_common_prefix
// lcp finds the longest common prefix of the input strings.
// It compares by bytes instead of runes (Unicode code points).
// It's up to the caller to do Unicode normalization if desired
// (e.g. see golang.org/x/text/unicode/norm).
func lcp(l []string) string {
	// Special cases first
	switch len(l) {
	case 0:
		return ""
	case 1:
		return l[0]
	}
	// LCP of min and max (lexigraphically)
	// is the LCP of the whole set.
	min, max := l[0], l[0]
	for _, s := range l[1:] {
		switch {
		case s < min:
			min = s
		case s > max:
			max = s
		}
	}
	for i := 0; i < len(min) && i < len(max); i++ {
		if min[i] != max[i] {
			return min[:i]
		}
	}
	// In the case where lengths are not equal but all bytes
	// are equal, min is the answer ("foo" < "foobar").
	return min
}
<commit_msg>fix: out of slice index when line is empty<commit_after>package readline

import (
	"errors"
	"fmt"
	"strings"
)

// TODO: print with a pretty format
func printCandidates(inst *Instance, cp Completer, candidates []string) {
	var prefix string
	args := strings.Fields(string(inst.line))
	if len(args) != 0 {
		prefix = args[len(args)-1]
	}
	inst.Print("\n")
	for _, n := range candidates {
		inst.Printf("%s\t", cp.modifyCandidate(prefix, n))
	}
	inst.Print("\n")
}

// @cp is the parent cmd node and must be cmd-completer.
// find the sub-completer whose name is @cmd
func findSubCompleter(inst *Instance, cmd string, cp Completer) Completer {
	if cp == nil {
		panic("Completer is nil")
	}

	inst.Log("findSubCompleter: find [%s] in %v\n", cmd,
		func(subs []Completer) []string {
			names := make([]string, 0)
			for _, c := range subs {
				names = append(names, c.name())
			}
			return names
		}(cp.subs()))

	if cp.isSp() {
		panic("Sp Completer must not have sub completers")
	}

	subs := cp.subs()
	for _, c := range subs {
		if !c.isSp() && c.name() == cmd {
			return c
		}
	}

	return nil
}

// search @cp's sub-completers and collect candidates
func getCandidatesFromSubs(inst *Instance, cp Completer) ([]string, bool, error) {
	if cp == nil {
		return nil, false, errors.New("Completer is nil")
	}

	var sp Completer
	var candidates []string
	subs := cp.subs()
	for _, c := range subs {
		if c.isSp() {
			sp = c
			break
		} else {
			candidates = append(candidates, c.name())
		}
	}

	if sp != nil {
		cans, end := sp.getCandidates(string(inst.line))
		return cans, end, nil
	}

	return candidates, true, nil
}

// find all commands in @cmds which have prefix of @arg
func getCandidatesByPrefix(inst *Instance, arg string, cp Completer) (Completer, []string, bool, error) {
	var candidates []string
	var sp Completer
	end := true
	currentCp := cp
	cps := cp.subs()
	for _, c := range cps {
		if c.isSp() {
			sp = c
		} else {
			if strings.HasPrefix(c.name(), arg) {
				candidates = append(candidates, c.name())
			}
		}
	}

	if len(candidates) == 0 && sp != nil {
		cans, _end := sp.getCandidates(string(inst.line))
		inst.Log("prefix: %s, end: %v, candidates: [%v]\n", arg, _end, cans)
		for _, v := range cans {
			if strings.HasPrefix(v, arg) {
				candidates = append(candidates, v)
			}
		}
		if len(candidates) == 1 {
			_end = true
		}
		end = _end
		currentCp = sp
	}

	return currentCp, candidates, end, nil
}

// check the inst.line and find the candidates
func getCandidates(inst *Instance) (Completer, []string, bool, error) {
	args := strings.Fields(string(inst.line))
	count := len(inst.line)
	inst.Log("args = %v, len(line) = %d\n", args, count)

	cp := inst.root
	candidates, end, err := getCandidatesFromSubs(inst, cp)
	if err != nil {
		return nil, nil, end, err
	}

	for i, arg := range args {

		lastArg := i == len(args)-1
		partialArg := inst.line[count-1] != ' '
		inst.Log("process cmd [%s], lastArg: %v, partialArg: %v\n",
			arg, lastArg, partialArg)

		if lastArg && partialArg {
			cp, candidates, end, err = getCandidatesByPrefix(inst, arg, cp)
			if err != nil {
				return nil, nil, end, err
			}
		} else {
			cp = findSubCompleter(inst, arg, cp)
			if cp == nil {
				inst.Log("can not find %s", arg)
				return nil, nil, false, errors.New(fmt.Sprintf("can not find %s", arg))
			}
			if lastArg {
				candidates, end, err = getCandidatesFromSubs(inst, cp)
				if err != nil {
					return nil, nil, end, err
				}
			}
		}
	}
	inst.Log("candidates: %v\n", candidates)
	return cp, candidates, end, nil
}

func doComplete(inst *Instance, candidate string) {
	args := strings.Fields(string(inst.line))
	count := len(args)
	if count == 0 {
		panic("when do complete, the input can not be empty fields")
	}
	todo := args[len(args)-1]
	index := strings.Index(candidate, todo) + len(todo)
	for i := index; i < len(candidate); i++ {
		inst.lineAdd(byte(candidate[i]))
	}
}

// [ls]: ls -> [ls ]
// return: whether is the auto-complete end
func completeWhole(inst *Instance, candidate string) bool {
	doComplete(inst, candidate)
	return true
}

// if all candidates have the same prefix, complete the common part
// if so, return true
// e.g. [clean], [clone]: c -> [cl]
func completePartial(inst *Instance, candidates []string) {
	prefix := lcp(candidates)
	if len(prefix) != 0 {
		doComplete(inst, prefix)
	}
}

// Via: https://rosettacode.org/wiki/Longest_common_prefix
// lcp finds the longest common prefix of the input strings.
// It compares by bytes instead of runes (Unicode code points).
// It's up to the caller to do Unicode normalization if desired
// (e.g. see golang.org/x/text/unicode/norm).
func lcp(l []string) string {
	// Special cases first
	switch len(l) {
	case 0:
		return ""
	case 1:
		return l[0]
	}
	// LCP of min and max (lexigraphically)
	// is the LCP of the whole set.
	min, max := l[0], l[0]
	for _, s := range l[1:] {
		switch {
		case s < min:
			min = s
		case s > max:
			max = s
		}
	}
	for i := 0; i < len(min) && i < len(max); i++ {
		if min[i] != max[i] {
			return min[:i]
		}
	}
	// In the case where lengths are not equal but all bytes
	// are equal, min is the answer ("foo" < "foobar").
	return min
}
<|endoftext|>