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

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package session

import (
	"context"
	"sync"
	"time"

	"github.com/opentracing/opentracing-go"
	"github.com/pingcap/errors"
	"github.com/pingcap/parser"
	"github.com/pingcap/parser/ast"
	"github.com/pingcap/parser/mysql"
	"github.com/pingcap/parser/terror"
	"github.com/pingcap/tidb/config"
	"github.com/pingcap/tidb/domain"
	"github.com/pingcap/tidb/executor"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/sessionctx"
	"github.com/pingcap/tidb/sessionctx/variable"
	"github.com/pingcap/tidb/types"
	"github.com/pingcap/tidb/util"
	"github.com/pingcap/tidb/util/chunk"
	"github.com/pingcap/tidb/util/logutil"
	"github.com/pingcap/tidb/util/sqlexec"
	"go.uber.org/zap"
)

type domainMap struct {
	domains map[string]*domain.Domain
	mu      sync.Mutex
}

func (dm *domainMap) Get(store kv.Storage) (d *domain.Domain, err error) {
	dm.mu.Lock()
	defer dm.mu.Unlock()

	// If this is the only domain instance, and the caller doesn't provide store.
	if len(dm.domains) == 1 && store == nil {
		for _, r := range dm.domains {
			return r, nil
		}
	}

	key := store.UUID()
	d = dm.domains[key]
	if d != nil {
		return
	}

	ddlLease := schemaLease
	statisticLease := statsLease
	err = util.RunWithRetry(util.DefaultMaxRetries, util.RetryInterval, func() (retry bool, err1 error) {
		logutil.BgLogger().Info("new domain",
			zap.String("store", store.UUID()),
			zap.Stringer("ddl lease", ddlLease),
			zap.Stringer("stats lease", statisticLease))
		factory := createSessionFunc(store)
		sysFactory := createSessionWithDomainFunc(store)
		d = domain.NewDomain(store, ddlLease, statisticLease, factory)
		err1 = d.Init(ddlLease, sysFactory)
		if err1 != nil {
			// If we don't clean it, there are some dirty data when retrying the function of Init.
			d.Close()
			logutil.BgLogger().Error("[ddl] init domain failed",
				zap.Error(err1))
		}
		return true, err1
	})
	if err != nil {
		return nil, err
	}
	dm.domains[key] = d

	return
}

func (dm *domainMap) Delete(store kv.Storage) {
	dm.mu.Lock()
	delete(dm.domains, store.UUID())
	dm.mu.Unlock()
}

var (
	domap = &domainMap{
		domains: map[string]*domain.Domain{},
	}
	// store.UUID()-> IfBootstrapped
	storeBootstrapped     = make(map[string]bool)
	storeBootstrappedLock sync.Mutex

	// schemaLease is the time for re-updating remote schema.
	// In online DDL, we must wait 2 * SchemaLease time to guarantee
	// all servers get the neweset schema.
	// Default schema lease time is 1 second, you can change it with a proper time,
	// but you must know that too little may cause badly performance degradation.
	// For production, you should set a big schema lease, like 300s+.
	schemaLease = 1 * time.Second

	// statsLease is the time for reload stats table.
	statsLease = 3 * time.Second
)

// SetSchemaLease changes the default schema lease time for DDL.
// This function is very dangerous, don't use it if you really know what you do.
// SetSchemaLease only affects not local storage after bootstrapped.
func SetSchemaLease(lease time.Duration) {
	schemaLease = lease
}

// SetStatsLease changes the default stats lease time for loading stats info.
func SetStatsLease(lease time.Duration) {
	statsLease = lease
}

// DisableStats4Test disables the stats for tests.
func DisableStats4Test() {
	statsLease = -1
}

// Parse parses a query string to raw ast.StmtNode.
func Parse(ctx sessionctx.Context, src string) ([]ast.StmtNode, error) {
	logutil.BgLogger().Debug("compiling", zap.String("source", src))
	charset, collation := ctx.GetSessionVars().GetCharsetInfo()
	p := parser.New()
	p.EnableWindowFunc(ctx.GetSessionVars().EnableWindowFunction)
	p.SetSQLMode(ctx.GetSessionVars().SQLMode)
	stmts, warns, err := p.Parse(src, charset, collation)
	for _, warn := range warns {
		ctx.GetSessionVars().StmtCtx.AppendWarning(warn)
	}
	if err != nil {
		logutil.BgLogger().Warn("compiling",
			zap.String("source", src),
			zap.Error(err))
		return nil, err
	}
	return stmts, nil
}

// Compile is safe for concurrent use by multiple goroutines.
func Compile(ctx context.Context, sctx sessionctx.Context, stmtNode ast.StmtNode) (sqlexec.Statement, error) {
	compiler := executor.Compiler{Ctx: sctx}
	stmt, err := compiler.Compile(ctx, stmtNode)
	return stmt, err
}

func finishStmt(ctx context.Context, sctx sessionctx.Context, se *session, sessVars *variable.SessionVars,
	meetsErr error, sql sqlexec.Statement) error {
	if meetsErr != nil {
		if !sessVars.InTxn() {
			logutil.BgLogger().Info("rollbackTxn for ddl/autocommit failed")
			se.RollbackTxn(ctx)
		} else if se.txn.Valid() && se.txn.IsPessimistic() && executor.ErrDeadlock.Equal(meetsErr) {
			logutil.BgLogger().Info("rollbackTxn for deadlock", zap.Uint64("txn", se.txn.StartTS()))
			se.RollbackTxn(ctx)
		}
		return meetsErr
	}

	if !sessVars.InTxn() {
		if err := se.CommitTxn(ctx); err != nil {
			if _, ok := sql.(*executor.ExecStmt).StmtNode.(*ast.CommitStmt); ok {
				err = errors.Annotatef(err, "previous statement: %s", se.GetSessionVars().PrevStmt)
			}
			return err
		}
		return nil
	}

	return checkStmtLimit(ctx, sctx, se)
}

func checkStmtLimit(ctx context.Context, sctx sessionctx.Context, se *session) error {
	// If the user insert, insert, insert ... but never commit, TiDB would OOM.
	// So we limit the statement count in a transaction here.
	var err error
	sessVars := se.GetSessionVars()
	history := GetHistory(sctx)
	if history.Count() > int(config.GetGlobalConfig().Performance.StmtCountLimit) {
		if !sessVars.BatchCommit {
			se.RollbackTxn(ctx)
			return errors.Errorf("statement count %d exceeds the transaction limitation, autocommit = %t",
				history.Count(), sctx.GetSessionVars().IsAutocommit())
		}
		err = se.NewTxn(ctx)
		// The transaction does not committed yet, we need to keep it in transaction.
		// The last history could not be "commit"/"rollback" statement.
		// It means it is impossible to start a new transaction at the end of the transaction.
		// Because after the server executed "commit"/"rollback" statement, the session is out of the transaction.
		sessVars.SetStatusFlag(mysql.ServerStatusInTrans, true)
	}
	return err
}

// runStmt executes the sqlexec.Statement and commit or rollback the current transaction.
func runStmt(ctx context.Context, sctx sessionctx.Context, s sqlexec.Statement) (rs sqlexec.RecordSet, err error) {
	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
		span1 := span.Tracer().StartSpan("session.runStmt", opentracing.ChildOf(span.Context()))
		span1.LogKV("sql", s.OriginText())
		defer span1.Finish()
		ctx = opentracing.ContextWithSpan(ctx, span1)
	}
	se := sctx.(*session)
	sessVars := se.GetSessionVars()
	// Save origTxnCtx here to avoid it reset in the transaction retry.
	origTxnCtx := sessVars.TxnCtx
	defer func() {
		// If it is not a select statement, we record its slow log here,
		// then it could include the transaction commit time.
		if rs == nil {
			s.(*executor.ExecStmt).LogSlowQuery(origTxnCtx.StartTS, err == nil, false)
			s.(*executor.ExecStmt).SummaryStmt()
			pps := types.CloneRow(sessVars.PreparedParams)
			sessVars.PrevStmt = executor.FormatSQL(s.OriginText(), pps)
		}
	}()

	err = se.checkTxnAborted(s)
	if err != nil {
		return nil, err
	}
	rs, err = s.Exec(ctx)
	sessVars.TxnCtx.StatementCount++
	if !s.IsReadOnly(sessVars) {
		// All the history should be added here.
		if err == nil && sessVars.TxnCtx.CouldRetry {
			GetHistory(sctx).Add(s, sessVars.StmtCtx)
		}

		// Handle the stmt commit/rollback.
		if txn, err1 := sctx.Txn(false); err1 == nil {
			if txn.Valid() {
				if err != nil {
					sctx.StmtRollback()
				} else {
					err = sctx.StmtCommit()
				}
			}
		} else {
			logutil.BgLogger().Error("get txn failed", zap.Error(err1))
		}
	}
	err = finishStmt(ctx, sctx, se, sessVars, err, s)

	if se.txn.pending() {
		// After run statement finish, txn state is still pending means the
		// statement never need a Txn(), such as:
		//
		// set @@tidb_general_log = 1
		// set @@autocommit = 0
		// select 1
		//
		// Reset txn state to invalid to dispose the pending start ts.
		se.txn.changeToInvalid()
	}
	return rs, err
}

// GetHistory get all stmtHistory in current txn. Exported only for test.
func GetHistory(ctx sessionctx.Context) *StmtHistory {
	hist, ok := ctx.GetSessionVars().TxnCtx.History.(*StmtHistory)
	if ok {
		return hist
	}
	hist = new(StmtHistory)
	ctx.GetSessionVars().TxnCtx.History = hist
	return hist
}

// GetRows4Test gets all the rows from a RecordSet, only used for test.
func GetRows4Test(ctx context.Context, sctx sessionctx.Context, rs sqlexec.RecordSet) ([]chunk.Row, error) {
	if rs == nil {
		return nil, nil
	}
	var rows []chunk.Row
	req := rs.NewChunk()
	// Must reuse `req` for imitating server.(*clientConn).writeChunks
	for {
		err := rs.Next(ctx, req)
		if err != nil {
			return nil, err
		}
		if req.NumRows() == 0 {
			break
		}

		iter := chunk.NewIterator4Chunk(req.CopyConstruct())
		for row := iter.Begin(); row != iter.End(); row = iter.Next() {
			rows = append(rows, row)
		}
	}
	return rows, nil
}

// ResultSetToStringSlice changes the RecordSet to [][]string.
func ResultSetToStringSlice(ctx context.Context, s Session, rs sqlexec.RecordSet) ([][]string, error) {
	rows, err := GetRows4Test(ctx, s, rs)
	if err != nil {
		return nil, err
	}
	err = rs.Close()
	if err != nil {
		return nil, err
	}
	sRows := make([][]string, len(rows))
	for i := range rows {
		row := rows[i]
		iRow := make([]string, row.Len())
		for j := 0; j < row.Len(); j++ {
			if row.IsNull(j) {
				iRow[j] = "<nil>"
			} else {
				d := row.GetDatum(j, &rs.Fields()[j].Column.FieldType)
				iRow[j], err = d.ToString()
				if err != nil {
					return nil, err
				}
			}
		}
		sRows[i] = iRow
	}
	return sRows, nil
}

// Session errors.
var (
	ErrForUpdateCantRetry = terror.ClassSession.New(codeForUpdateCantRetry,
		mysql.MySQLErrName[mysql.ErrForUpdateCantRetry])
)

const (
	codeForUpdateCantRetry terror.ErrCode = mysql.ErrForUpdateCantRetry
)

func init() {
	sessionMySQLErrCodes := map[terror.ErrCode]uint16{
		codeForUpdateCantRetry: mysql.ErrForUpdateCantRetry,
	}
	terror.ErrClassToMySQLCodes[terror.ClassSession] = sessionMySQLErrCodes
}
<commit_msg>executor: fix data race in test (#12910)<commit_after>// Copyright 2013 The ql Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSES/QL-LICENSE file.

// Copyright 2015 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package session

import (
	"context"
	"sync"
	"sync/atomic"
	"time"

	"github.com/opentracing/opentracing-go"
	"github.com/pingcap/errors"
	"github.com/pingcap/parser"
	"github.com/pingcap/parser/ast"
	"github.com/pingcap/parser/mysql"
	"github.com/pingcap/parser/terror"
	"github.com/pingcap/tidb/config"
	"github.com/pingcap/tidb/domain"
	"github.com/pingcap/tidb/executor"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/sessionctx"
	"github.com/pingcap/tidb/sessionctx/variable"
	"github.com/pingcap/tidb/types"
	"github.com/pingcap/tidb/util"
	"github.com/pingcap/tidb/util/chunk"
	"github.com/pingcap/tidb/util/logutil"
	"github.com/pingcap/tidb/util/sqlexec"
	"go.uber.org/zap"
)

type domainMap struct {
	domains map[string]*domain.Domain
	mu      sync.Mutex
}

func (dm *domainMap) Get(store kv.Storage) (d *domain.Domain, err error) {
	dm.mu.Lock()
	defer dm.mu.Unlock()

	// If this is the only domain instance, and the caller doesn't provide store.
	if len(dm.domains) == 1 && store == nil {
		for _, r := range dm.domains {
			return r, nil
		}
	}

	key := store.UUID()
	d = dm.domains[key]
	if d != nil {
		return
	}

	ddlLease := time.Duration(atomic.LoadInt64(&schemaLease))
	statisticLease := time.Duration(atomic.LoadInt64(&statsLease))
	err = util.RunWithRetry(util.DefaultMaxRetries, util.RetryInterval, func() (retry bool, err1 error) {
		logutil.BgLogger().Info("new domain",
			zap.String("store", store.UUID()),
			zap.Stringer("ddl lease", ddlLease),
			zap.Stringer("stats lease", statisticLease))
		factory := createSessionFunc(store)
		sysFactory := createSessionWithDomainFunc(store)
		d = domain.NewDomain(store, ddlLease, statisticLease, factory)
		err1 = d.Init(ddlLease, sysFactory)
		if err1 != nil {
			// If we don't clean it, there are some dirty data when retrying the function of Init.
			d.Close()
			logutil.BgLogger().Error("[ddl] init domain failed",
				zap.Error(err1))
		}
		return true, err1
	})
	if err != nil {
		return nil, err
	}
	dm.domains[key] = d

	return
}

func (dm *domainMap) Delete(store kv.Storage) {
	dm.mu.Lock()
	delete(dm.domains, store.UUID())
	dm.mu.Unlock()
}

var (
	domap = &domainMap{
		domains: map[string]*domain.Domain{},
	}
	// store.UUID()-> IfBootstrapped
	storeBootstrapped     = make(map[string]bool)
	storeBootstrappedLock sync.Mutex

	// schemaLease is the time for re-updating remote schema.
	// In online DDL, we must wait 2 * SchemaLease time to guarantee
	// all servers get the neweset schema.
	// Default schema lease time is 1 second, you can change it with a proper time,
	// but you must know that too little may cause badly performance degradation.
	// For production, you should set a big schema lease, like 300s+.
	schemaLease = int64(1 * time.Second)

	// statsLease is the time for reload stats table.
	statsLease = int64(3 * time.Second)
)

// SetSchemaLease changes the default schema lease time for DDL.
// This function is very dangerous, don't use it if you really know what you do.
// SetSchemaLease only affects not local storage after bootstrapped.
func SetSchemaLease(lease time.Duration) {
	atomic.StoreInt64(&schemaLease, int64(lease))
}

// SetStatsLease changes the default stats lease time for loading stats info.
func SetStatsLease(lease time.Duration) {
	atomic.StoreInt64(&statsLease, int64(lease))
}

// DisableStats4Test disables the stats for tests.
func DisableStats4Test() {
	SetStatsLease(-1)
}

// Parse parses a query string to raw ast.StmtNode.
func Parse(ctx sessionctx.Context, src string) ([]ast.StmtNode, error) {
	logutil.BgLogger().Debug("compiling", zap.String("source", src))
	charset, collation := ctx.GetSessionVars().GetCharsetInfo()
	p := parser.New()
	p.EnableWindowFunc(ctx.GetSessionVars().EnableWindowFunction)
	p.SetSQLMode(ctx.GetSessionVars().SQLMode)
	stmts, warns, err := p.Parse(src, charset, collation)
	for _, warn := range warns {
		ctx.GetSessionVars().StmtCtx.AppendWarning(warn)
	}
	if err != nil {
		logutil.BgLogger().Warn("compiling",
			zap.String("source", src),
			zap.Error(err))
		return nil, err
	}
	return stmts, nil
}

// Compile is safe for concurrent use by multiple goroutines.
func Compile(ctx context.Context, sctx sessionctx.Context, stmtNode ast.StmtNode) (sqlexec.Statement, error) {
	compiler := executor.Compiler{Ctx: sctx}
	stmt, err := compiler.Compile(ctx, stmtNode)
	return stmt, err
}

func finishStmt(ctx context.Context, sctx sessionctx.Context, se *session, sessVars *variable.SessionVars,
	meetsErr error, sql sqlexec.Statement) error {
	if meetsErr != nil {
		if !sessVars.InTxn() {
			logutil.BgLogger().Info("rollbackTxn for ddl/autocommit failed")
			se.RollbackTxn(ctx)
		} else if se.txn.Valid() && se.txn.IsPessimistic() && executor.ErrDeadlock.Equal(meetsErr) {
			logutil.BgLogger().Info("rollbackTxn for deadlock", zap.Uint64("txn", se.txn.StartTS()))
			se.RollbackTxn(ctx)
		}
		return meetsErr
	}

	if !sessVars.InTxn() {
		if err := se.CommitTxn(ctx); err != nil {
			if _, ok := sql.(*executor.ExecStmt).StmtNode.(*ast.CommitStmt); ok {
				err = errors.Annotatef(err, "previous statement: %s", se.GetSessionVars().PrevStmt)
			}
			return err
		}
		return nil
	}

	return checkStmtLimit(ctx, sctx, se)
}

func checkStmtLimit(ctx context.Context, sctx sessionctx.Context, se *session) error {
	// If the user insert, insert, insert ... but never commit, TiDB would OOM.
	// So we limit the statement count in a transaction here.
	var err error
	sessVars := se.GetSessionVars()
	history := GetHistory(sctx)
	if history.Count() > int(config.GetGlobalConfig().Performance.StmtCountLimit) {
		if !sessVars.BatchCommit {
			se.RollbackTxn(ctx)
			return errors.Errorf("statement count %d exceeds the transaction limitation, autocommit = %t",
				history.Count(), sctx.GetSessionVars().IsAutocommit())
		}
		err = se.NewTxn(ctx)
		// The transaction does not committed yet, we need to keep it in transaction.
		// The last history could not be "commit"/"rollback" statement.
		// It means it is impossible to start a new transaction at the end of the transaction.
		// Because after the server executed "commit"/"rollback" statement, the session is out of the transaction.
		sessVars.SetStatusFlag(mysql.ServerStatusInTrans, true)
	}
	return err
}

// runStmt executes the sqlexec.Statement and commit or rollback the current transaction.
func runStmt(ctx context.Context, sctx sessionctx.Context, s sqlexec.Statement) (rs sqlexec.RecordSet, err error) {
	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
		span1 := span.Tracer().StartSpan("session.runStmt", opentracing.ChildOf(span.Context()))
		span1.LogKV("sql", s.OriginText())
		defer span1.Finish()
		ctx = opentracing.ContextWithSpan(ctx, span1)
	}
	se := sctx.(*session)
	sessVars := se.GetSessionVars()
	// Save origTxnCtx here to avoid it reset in the transaction retry.
	origTxnCtx := sessVars.TxnCtx
	defer func() {
		// If it is not a select statement, we record its slow log here,
		// then it could include the transaction commit time.
		if rs == nil {
			s.(*executor.ExecStmt).LogSlowQuery(origTxnCtx.StartTS, err == nil, false)
			s.(*executor.ExecStmt).SummaryStmt()
			pps := types.CloneRow(sessVars.PreparedParams)
			sessVars.PrevStmt = executor.FormatSQL(s.OriginText(), pps)
		}
	}()

	err = se.checkTxnAborted(s)
	if err != nil {
		return nil, err
	}
	rs, err = s.Exec(ctx)
	sessVars.TxnCtx.StatementCount++
	if !s.IsReadOnly(sessVars) {
		// All the history should be added here.
		if err == nil && sessVars.TxnCtx.CouldRetry {
			GetHistory(sctx).Add(s, sessVars.StmtCtx)
		}

		// Handle the stmt commit/rollback.
		if txn, err1 := sctx.Txn(false); err1 == nil {
			if txn.Valid() {
				if err != nil {
					sctx.StmtRollback()
				} else {
					err = sctx.StmtCommit()
				}
			}
		} else {
			logutil.BgLogger().Error("get txn failed", zap.Error(err1))
		}
	}
	err = finishStmt(ctx, sctx, se, sessVars, err, s)

	if se.txn.pending() {
		// After run statement finish, txn state is still pending means the
		// statement never need a Txn(), such as:
		//
		// set @@tidb_general_log = 1
		// set @@autocommit = 0
		// select 1
		//
		// Reset txn state to invalid to dispose the pending start ts.
		se.txn.changeToInvalid()
	}
	return rs, err
}

// GetHistory get all stmtHistory in current txn. Exported only for test.
func GetHistory(ctx sessionctx.Context) *StmtHistory {
	hist, ok := ctx.GetSessionVars().TxnCtx.History.(*StmtHistory)
	if ok {
		return hist
	}
	hist = new(StmtHistory)
	ctx.GetSessionVars().TxnCtx.History = hist
	return hist
}

// GetRows4Test gets all the rows from a RecordSet, only used for test.
func GetRows4Test(ctx context.Context, sctx sessionctx.Context, rs sqlexec.RecordSet) ([]chunk.Row, error) {
	if rs == nil {
		return nil, nil
	}
	var rows []chunk.Row
	req := rs.NewChunk()
	// Must reuse `req` for imitating server.(*clientConn).writeChunks
	for {
		err := rs.Next(ctx, req)
		if err != nil {
			return nil, err
		}
		if req.NumRows() == 0 {
			break
		}

		iter := chunk.NewIterator4Chunk(req.CopyConstruct())
		for row := iter.Begin(); row != iter.End(); row = iter.Next() {
			rows = append(rows, row)
		}
	}
	return rows, nil
}

// ResultSetToStringSlice changes the RecordSet to [][]string.
func ResultSetToStringSlice(ctx context.Context, s Session, rs sqlexec.RecordSet) ([][]string, error) {
	rows, err := GetRows4Test(ctx, s, rs)
	if err != nil {
		return nil, err
	}
	err = rs.Close()
	if err != nil {
		return nil, err
	}
	sRows := make([][]string, len(rows))
	for i := range rows {
		row := rows[i]
		iRow := make([]string, row.Len())
		for j := 0; j < row.Len(); j++ {
			if row.IsNull(j) {
				iRow[j] = "<nil>"
			} else {
				d := row.GetDatum(j, &rs.Fields()[j].Column.FieldType)
				iRow[j], err = d.ToString()
				if err != nil {
					return nil, err
				}
			}
		}
		sRows[i] = iRow
	}
	return sRows, nil
}

// Session errors.
var (
	ErrForUpdateCantRetry = terror.ClassSession.New(codeForUpdateCantRetry,
		mysql.MySQLErrName[mysql.ErrForUpdateCantRetry])
)

const (
	codeForUpdateCantRetry terror.ErrCode = mysql.ErrForUpdateCantRetry
)

func init() {
	sessionMySQLErrCodes := map[terror.ErrCode]uint16{
		codeForUpdateCantRetry: mysql.ErrForUpdateCantRetry,
	}
	terror.ErrClassToMySQLCodes[terror.ClassSession] = sessionMySQLErrCodes
}
<|endoftext|>


<commit_before>package phantomjs

import "testing"

func TestGet(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}
}

func TestRefresh(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	err = session.Refresh()
	if err != nil {
		t.Fatal(err)
	}
}

func TestGetCurrentUrl(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	urlStr := "https://www.baidu.com/"
	err = session.Get(urlStr)
	if err != nil {
		t.Fatal(err)
	}

	currentUrlStr, err := session.GetCurrentUrl()
	if err != nil {
		t.Fatal(err)
	}
	if currentUrlStr != urlStr {
		t.Fatalf("current url %s not equal %s", currentUrlStr, urlStr)
	}
}

func TestGetTitle(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	urlStr := "https://www.baidu.com/"
	err = session.Get(urlStr)
	if err != nil {
		t.Fatal(err)
	}

	title, err := session.GetTitle()
	if err != nil {
		t.Fatal(err)
	}
	if title != "百度一下，你就知道" {
		t.Fatalf("the title '%s' is not as excepted", title)
	}
}

func TestGetPageSource(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	urlStr := "https://www.baidu.com/"
	err = session.Get(urlStr)
	if err != nil {
		t.Fatal(err)
	}

	source, err := session.GetPageSource()
	if err != nil {
		t.Fatal(err)
	}
	if source == "" {
		t.Fatal("get empty webpage source")
	}
}

func TestWindowSize(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	width, height, err := session.GetWindowSize()
	if err != nil {
		t.Fatal(err)
	}
	err = session.SetWindowSize(1920, 1080)
	if err != nil {
		t.Fatal(err)
	}
	width, height, err = session.GetWindowSize()
	if width != 1920 && height != 1080 {
		t.Fatal("set window size or get window size failed.")
	}
}

func TestGetWindowPosition(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	x, y, err := session.GetWindowPosition()
	if err != nil {
		t.Fatal(err)
	}
	if x != 0 && y != 0 {
		t.Fatal("get invalid window position")
	}
}

func TestSetWindowPosition(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.SetWindowPosition(10, 10)
	if err != nil {
		t.Fatal(err)
	}
	if err != nil {
		t.Fatal(err)
	}
}

func TestAddCookie(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	cookie := Cookie{Name: "test", Path: "/", Value: "hello, world", Domain: ".baidu.com"}
	err = session.AddCookie(&cookie)
	if err != nil {
		t.Fatal(err)
	}
}

func TestGetAllCookies(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}
	cookies, err := session.GetAllCookies()
	if err != nil {
		t.Fatal(err)
	}
	if len(cookies) == 0 {
		t.Fatal("get cookies failed")
	}
}

func TestGetCookie(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	cookie := Cookie{Name: "test", Path: "/", Value: "hello, world", Domain: ".baidu.com"}
	err = session.AddCookie(&cookie)
	if err != nil {
		t.Fatal(err)
	}

	ck, err := session.GetCookie("test")
	if err != nil {
		t.Fatal(err)
	}
	if ck == nil || ck.Name != cookie.Name || ck.Value != cookie.Value {
		t.Fatal("get cookie test failed")
	}
}

func TestDeleteAllCookies(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	err = session.DeleteAllCookies()
	if err != nil {
		t.Fatal(err)
	}

	cookies, err := session.GetAllCookies()
	if err != nil {
		t.Fatal(err)
	}
	if len(cookies) != 0 {
		t.Fatal("delete cookies failed")
	}
}

func TestSetPageLoadTimeouts(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.SetPageLoadTimeouts(1000)
	if err != nil {
		t.Fatal(err)
	}
}

func TestSetScriptTimeout(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(10000, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.SetScriptTimeout(1000)
	if err != nil {
		t.Fatal(err)
	}
}
<commit_msg>set test phantomjs port to auto select<commit_after>package phantomjs

import "testing"

func TestGet(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}
}

func TestRefresh(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	err = session.Refresh()
	if err != nil {
		t.Fatal(err)
	}
}

func TestGetCurrentUrl(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	urlStr := "https://www.baidu.com/"
	err = session.Get(urlStr)
	if err != nil {
		t.Fatal(err)
	}

	currentUrlStr, err := session.GetCurrentUrl()
	if err != nil {
		t.Fatal(err)
	}
	if currentUrlStr != urlStr {
		t.Fatalf("current url %s not equal %s", currentUrlStr, urlStr)
	}
}

func TestGetTitle(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	urlStr := "https://www.baidu.com/"
	err = session.Get(urlStr)
	if err != nil {
		t.Fatal(err)
	}

	title, err := session.GetTitle()
	if err != nil {
		t.Fatal(err)
	}
	if title != "百度一下，你就知道" {
		t.Fatalf("the title '%s' is not as excepted", title)
	}
}

func TestGetPageSource(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	urlStr := "https://www.baidu.com/"
	err = session.Get(urlStr)
	if err != nil {
		t.Fatal(err)
	}

	source, err := session.GetPageSource()
	if err != nil {
		t.Fatal(err)
	}
	if source == "" {
		t.Fatal("get empty webpage source")
	}
}

func TestWindowSize(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	width, height, err := session.GetWindowSize()
	if err != nil {
		t.Fatal(err)
	}
	err = session.SetWindowSize(1920, 1080)
	if err != nil {
		t.Fatal(err)
	}
	width, height, err = session.GetWindowSize()
	if width != 1920 && height != 1080 {
		t.Fatal("set window size or get window size failed.")
	}
}

func TestGetWindowPosition(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	x, y, err := session.GetWindowPosition()
	if err != nil {
		t.Fatal(err)
	}
	if x != 0 && y != 0 {
		t.Fatal("get invalid window position")
	}
}

func TestSetWindowPosition(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.SetWindowPosition(10, 10)
	if err != nil {
		t.Fatal(err)
	}
	if err != nil {
		t.Fatal(err)
	}
}

func TestAddCookie(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	cookie := Cookie{Name: "test", Path: "/", Value: "hello, world", Domain: ".baidu.com"}
	err = session.AddCookie(&cookie)
	if err != nil {
		t.Fatal(err)
	}
}

func TestGetAllCookies(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}
	cookies, err := session.GetAllCookies()
	if err != nil {
		t.Fatal(err)
	}
	if len(cookies) == 0 {
		t.Fatal("get cookies failed")
	}
}

func TestGetCookie(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	cookie := Cookie{Name: "test", Path: "/", Value: "hello, world", Domain: ".baidu.com"}
	err = session.AddCookie(&cookie)
	if err != nil {
		t.Fatal(err)
	}

	ck, err := session.GetCookie("test")
	if err != nil {
		t.Fatal(err)
	}
	if ck == nil || ck.Name != cookie.Name || ck.Value != cookie.Value {
		t.Fatal("get cookie test failed")
	}
}

func TestDeleteAllCookies(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.Get("https://www.baidu.com")
	if err != nil {
		t.Fatal(err)
	}

	err = session.DeleteAllCookies()
	if err != nil {
		t.Fatal(err)
	}

	cookies, err := session.GetAllCookies()
	if err != nil {
		t.Fatal(err)
	}
	if len(cookies) != 0 {
		t.Fatal("delete cookies failed")
	}
}

func TestSetPageLoadTimeouts(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.SetPageLoadTimeouts(1000)
	if err != nil {
		t.Fatal(err)
	}
}

func TestSetScriptTimeout(t *testing.T) {
	options := &Options{}
	phantomJS, err := NewPhantomJS(0, options)
	if err != nil {
		t.Fatal(err)
	}
	defer phantomJS.Quit()

	session, err := phantomJS.NewSession(nil)
	if err != nil {
		t.Fatal(err)
	}
	defer session.Close()

	err = session.SetScriptTimeout(0)
	if err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"regexp"
	"testing"

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

func init() {
	printerPkgs = []struct {
		path string
		code string
	}{
		{"fmt", `fmt.Printf("%#v\n", x)`},
	}
}

func TestRun_import(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		":import encoding/json",
		"b, err := json.Marshal(nil)",
		"string(b)",
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, `[]byte{0x6e, 0x75, 0x6c, 0x6c}
<nil>
"null"
`, stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_QuickFix_evaluated_but_not_used(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`[]byte("")`,
		`make([]int, 0)`,
		`1+1`,
		`func() {}`,
		`(4 & (1 << 1))`,
		`1`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	r := regexp.MustCompile(`0x[0-9a-f]+`)
	require.Equal(t, `[]byte{}
[]int{}
2
(func())(...)
0
1
`, r.ReplaceAllString(stdout.String(), "..."))
	require.Equal(t, "", stderr.String())
}

func TestRun_QuickFix_used_as_value(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`:import log`,
		`a := 1`,
		`log.SetPrefix("")`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, "1\n", stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_FixImports(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	autoimport := true
	flagAutoImport = &autoimport

	codes := []string{
		`filepath.Join("a", "b")`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, "\"a/b\"\n", stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestIncludePackage(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	err = s.includePackage("github.com/motemen/gore/gocode")
	require.NoError(t, err)

	err = s.Eval("Completer{}")
	require.NoError(t, err)
}

func TestRun_Copy(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`a := []string{"hello", "world"}`,
		`b := []string{"goodbye", "world"}`,
		`copy(a, b)`,
		`if (a[0] != "goodbye") {
			panic("should be copied")
		}`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, `[]string{"hello", "world"}
[]string{"goodbye", "world"}
2
`, stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_Const(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`const ( a = iota; b )`,
		`a`,
		`b`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, "0\n1\n", stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_Error(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`foo`,
		`len(100)`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.Error(t, err)
	}

	require.Equal(t, "", stdout.String())
	require.Equal(t, `undefined: foo
invalid argument 100 (type int) for len
`, stderr.String())
}
<commit_msg>add tests for purity analysis and evaluated but not used error<commit_after>package main

import (
	"bytes"
	"regexp"
	"testing"

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

func init() {
	printerPkgs = []struct {
		path string
		code string
	}{
		{"fmt", `fmt.Printf("%#v\n", x)`},
	}
}

func TestRun_import(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		":import encoding/json",
		"b, err := json.Marshal(nil)",
		"string(b)",
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, `[]byte{0x6e, 0x75, 0x6c, 0x6c}
<nil>
"null"
`, stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_QuickFix_evaluated_but_not_used(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`[]byte("")`,
		`make([]int, 0)`,
		`1+1`,
		`func() {}`,
		`(4 & (1 << 1))`,
		`1`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	r := regexp.MustCompile(`0x[0-9a-f]+`)
	require.Equal(t, `[]byte{}
[]int{}
2
(func())(...)
0
1
`, r.ReplaceAllString(stdout.String(), "..."))
	require.Equal(t, "", stderr.String())
}

func TestRun_QuickFix_used_as_value(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`:import log`,
		`a := 1`,
		`log.SetPrefix("")`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, "1\n", stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_FixImports(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	autoimport := true
	flagAutoImport = &autoimport

	codes := []string{
		`filepath.Join("a", "b")`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, "\"a/b\"\n", stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestIncludePackage(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	err = s.includePackage("github.com/motemen/gore/gocode")
	require.NoError(t, err)

	err = s.Eval("Completer{}")
	require.NoError(t, err)
}

func TestRun_Copy(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`a := []string{"hello", "world"}`,
		`b := []string{"goodbye", "world"}`,
		`copy(a, b)`,
		`if (a[0] != "goodbye") {
			panic("should be copied")
		}`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, `[]string{"hello", "world"}
[]string{"goodbye", "world"}
2
`, stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_Const(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`const ( a = iota; b )`,
		`a`,
		`b`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.NoError(t, err)
	}

	require.Equal(t, "0\n1\n", stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_NotUsed(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`f := func() []int { return []int{1, 2, 3} }`,
		`len(f())`,
		`3`,
		`len(f()) + len(f())`,
		`var x int`,
		`g := func() int { x++; return 128 }`,
		`g() + g()`,
		`g() * g()`,
		`x`,
	}

	for _, code := range codes {
		_ = s.Eval(code)
	}

	r := regexp.MustCompile(`0x[0-9a-f]+`)
	require.Equal(t, `(func() []int)(...)
3
3
6
(func() int)(...)
256
16384
4
`, r.ReplaceAllString(stdout.String(), "..."))
	require.Equal(t, "", stderr.String())
}

func TestRun_MultipleValues(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`var err error`,
		`:import fmt`,
		`fmt.Print()`,
		`fmt.Print()`,
		`:import io`,
		`_, err = func() (int, error) { return 0, io.EOF }()`,
		`err.Error()`,
		`var x int`,
		`x, err = 10, fmt.Errorf("test")`,
		`x`,
		`err.Error()`,
	}

	for _, code := range codes {
		_ = s.Eval(code)
	}

	require.Equal(t, `0
<nil>
0
<nil>
&errors.errorString{s:"EOF"}
"EOF"
10
&errors.errorString{s:"test"}
10
"test"
`, stdout.String())
	require.Equal(t, "", stderr.String())
}

func TestRun_Error(t *testing.T) {
	stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
	s, err := NewSession(stdout, stderr)
	defer s.Clear()
	require.NoError(t, err)

	codes := []string{
		`foo`,
		`len(100)`,
	}

	for _, code := range codes {
		err := s.Eval(code)
		require.Error(t, err)
	}

	require.Equal(t, "", stdout.String())
	require.Equal(t, `undefined: foo
invalid argument 100 (type int) for len
`, stderr.String())
}
<|endoftext|>


<commit_before>package gockle

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/gocql/gocql"
	"github.com/maraino/go-mock"
)

func TestNewSession(t *testing.T) {
	for _, test := range []*gocql.Session{} {
		var a = NewSession(test)

		if e := (session{s: test}); a != e {
			t.Errorf("Actual %v, expected %v", a, e)
		}
	}
}

func TestSessionMock(t *testing.T) {
	var m, e = &SessionMock{}, fmt.Errorf("e")

	testMock(t, m, &m.Mock, []struct {
		method    string
		arguments []interface{}
		results   []interface{}
	}{
		{"Close", nil, nil},
		{"Columns", []interface{}{"", ""}, []interface{}{map[string]gocql.TypeInfo(nil), nil}},
		{"Columns", []interface{}{"a", "b"}, []interface{}{map[string]gocql.TypeInfo{"c": gocql.NativeType{}}, e}},
		{"QueryBatch", []interface{}{BatchKind(0)}, []interface{}{(*batch)(nil)}},
		{"QueryBatch", []interface{}{BatchKind(1)}, []interface{}{&batch{}}},
		{"QueryExecute", []interface{}{"", []interface{}(nil)}, []interface{}{nil}},
		{"QueryExecute", []interface{}{"a", []interface{}{1}}, []interface{}{e}},
		{"QueryIterate", []interface{}{"", []interface{}(nil)}, []interface{}{(*iterator)(nil)}},
		{"QueryIterate", []interface{}{"a", []interface{}{1}}, []interface{}{iterator{}}},
		{"QueryScan", []interface{}{"", []interface{}(nil), []interface{}(nil)}, []interface{}{nil}},
		{"QueryScan", []interface{}{"a", []interface{}{1}, []interface{}{1}}, []interface{}{e}},
		{"QueryScanMap", []interface{}{"", []interface{}(nil), map[string]interface{}(nil)}, []interface{}{nil}},
		{"QueryScanMap", []interface{}{"a", []interface{}{1}, map[string]interface{}{"b": 2}}, []interface{}{e}},
		{"QueryScanMapTransaction", []interface{}{"", []interface{}(nil), map[string]interface{}(nil)}, []interface{}{false, nil}},
		{"QueryScanMapTransaction", []interface{}{"a", []interface{}{1}, map[string]interface{}{"b": 2}}, []interface{}{true, e}},
		{"QuerySliceMap", []interface{}{"", []interface{}(nil)}, []interface{}{[]map[string]interface{}(nil), nil}},
		{"QuerySliceMap", []interface{}{"a", []interface{}{1}}, []interface{}{[]map[string]interface{}{{"b": 2}}, e}},
		{"Tables", []interface{}{""}, []interface{}{[]string(nil), nil}},
		{"Tables", []interface{}{"a"}, []interface{}{[]string{"b"}, e}},
	})
}

func testMock(t *testing.T, i interface{}, m *mock.Mock, tests []struct {
	method    string
	arguments []interface{}
	results   []interface{}
}) {
	var v = reflect.ValueOf(i)

	for _, test := range tests {
		t.Log("Test:", test)
		m.Reset()
		m.When(test.method, test.arguments...).Return(test.results...)

		var vs []reflect.Value

		for _, a := range test.arguments {
			vs = append(vs, reflect.ValueOf(a))
		}

		var method = v.MethodByName(test.method)

		if method.Type().IsVariadic() {
			vs = method.CallSlice(vs)
		} else {
			vs = method.Call(vs)
		}

		var is []interface{}

		for _, v := range vs {
			is = append(is, v.Interface())
		}

		if !reflect.DeepEqual(is, test.results) {
			t.Errorf("Actual %v, expected %v", is, test.results)
		}
	}
}
<commit_msg>Add Session tests<commit_after>package gockle

import (
	"flag"
	"fmt"
	"reflect"
	"testing"
	"time"

	"github.com/facebookgo/flagenv"
	"github.com/gocql/gocql"
	"github.com/maraino/go-mock"
)

const timeout = 5 * time.Second

const (
	ksCreate  = "create keyspace gockle_test with replication = {'class': 'SimpleStrategy', 'replication_factor': 1};"
	ksDrop    = "drop keyspace gockle_test"
	ksDropIf  = "drop keyspace if exists gockle_test"
	rowInsert = "insert into gockle_test.test (id, n) values (1, 2)"
	tabCreate = "create table gockle_test.test(id int primary key, n int)"
	tabDrop   = "drop table gockle_test.test"
)

var protoVersion = flag.Int("proto-version", 4, "CQL protocol version")

func TestNewSession(t *testing.T) {
	if a, e := NewSession(nil), (session{}); a != e {
		t.Errorf("Actual session %v, expected %v", a, e)
	}

	var c = gocql.NewCluster("localhost")

	c.ProtoVersion = *protoVersion
	c.Timeout = timeout

	var s, err = c.CreateSession()

	if err != nil {
		t.Skip(err)
	}

	if a, e := NewSession(s), (session{s: s}); a != e {
		t.Errorf("Actual session %v, expected %v", a, e)
	}
}

func TestSessionMetadata(t *testing.T) {
	var exec = func(s Session, q string) {
		if err := s.QueryExecute(q); err != nil {
			t.Errorf("Actual error %v, expected no error", err)
		}
	}

	var s = newSession(t)

	exec(s, ksDropIf)
	exec(s, ksCreate)
	exec(s, tabCreate)

	s = newSession(t)

	if a, err := s.Tables("gockle_test"); err == nil {
		if e := ([]string{"test"}); !reflect.DeepEqual(a, e) {
			t.Errorf("Actual tables %v, expected %v", a, e)
		}
	} else {
		t.Errorf("Actual error %v, expected no error", err)
	}

	if a, err := s.Columns("gockle_test", "test"); err == nil {
		var ts = map[string]gocql.Type{"id": gocql.TypeInt, "n": gocql.TypeInt}

		if la, le := len(a), len(ts); la == le {
			for n, at := range a {
				if et, ok := ts[n]; ok {
					if at.Type() != et {
						t.Errorf("Actual type %v, expected %v", at, et)
					}
				} else {
					t.Errorf("Actual name %v invalid, expected valid", n)
				}
			}
		} else {
			t.Errorf("Actual count %v, expected %v", la, le)
		}
	} else {
		t.Errorf("Actual error %v, expected no error", err)
	}

	exec(s, tabDrop)
	exec(s, ksDrop)

	s.Close()
}

func TestSessionMock(t *testing.T) {
	var m, e = &SessionMock{}, fmt.Errorf("e")

	testMock(t, m, &m.Mock, []struct {
		method    string
		arguments []interface{}
		results   []interface{}
	}{
		{"Close", nil, nil},
		{"Columns", []interface{}{"", ""}, []interface{}{map[string]gocql.TypeInfo(nil), nil}},
		{"Columns", []interface{}{"a", "b"}, []interface{}{map[string]gocql.TypeInfo{"c": gocql.NativeType{}}, e}},
		{"QueryBatch", []interface{}{BatchKind(0)}, []interface{}{(*batch)(nil)}},
		{"QueryBatch", []interface{}{BatchKind(1)}, []interface{}{&batch{}}},
		{"QueryExecute", []interface{}{"", []interface{}(nil)}, []interface{}{nil}},
		{"QueryExecute", []interface{}{"a", []interface{}{1}}, []interface{}{e}},
		{"QueryIterate", []interface{}{"", []interface{}(nil)}, []interface{}{(*iterator)(nil)}},
		{"QueryIterate", []interface{}{"a", []interface{}{1}}, []interface{}{iterator{}}},
		{"QueryScan", []interface{}{"", []interface{}(nil), []interface{}(nil)}, []interface{}{nil}},
		{"QueryScan", []interface{}{"a", []interface{}{1}, []interface{}{1}}, []interface{}{e}},
		{"QueryScanMap", []interface{}{"", []interface{}(nil), map[string]interface{}(nil)}, []interface{}{nil}},
		{"QueryScanMap", []interface{}{"a", []interface{}{1}, map[string]interface{}{"b": 2}}, []interface{}{e}},
		{"QueryScanMapTransaction", []interface{}{"", []interface{}(nil), map[string]interface{}(nil)}, []interface{}{false, nil}},
		{"QueryScanMapTransaction", []interface{}{"a", []interface{}{1}, map[string]interface{}{"b": 2}}, []interface{}{true, e}},
		{"QuerySliceMap", []interface{}{"", []interface{}(nil)}, []interface{}{[]map[string]interface{}(nil), nil}},
		{"QuerySliceMap", []interface{}{"a", []interface{}{1}}, []interface{}{[]map[string]interface{}{{"b": 2}}, e}},
		{"Tables", []interface{}{""}, []interface{}{[]string(nil), nil}},
		{"Tables", []interface{}{"a"}, []interface{}{[]string{"b"}, e}},
	})
}

func TestSessionQuery(t *testing.T) {
	var s = newSession(t)

	var exec = func(q string) {
		if err := s.QueryExecute(q); err != nil {
			t.Fatalf("Actual error %v, expected no error", err)
		}
	}

	exec(ksDropIf)
	exec(ksCreate)
	exec(tabCreate)
	exec(rowInsert)

	// QueryBatch
	if s.QueryBatch(BatchKind(0)) == nil {
		t.Error("Actual batch nil, expected not nil")
	}

	// QueryIterate
	if s.QueryIterate("select * from gockle_test.test") == nil {
		t.Error("Actual iterator nil, expected not nil")
	}

	// QueryScan
	var id, n int

	if err := s.QueryScan("select id, n from gockle_test.test", nil, []interface{}{&id, &n}); err == nil {
		if id != 1 {
			t.Errorf("Actual id %v, expected 1", id)
		}

		if n != 2 {
			t.Errorf("Actual n %v, expected 2", n)
		}
	} else {
		t.Errorf("Actual error %v, expected no error", err)
	}

	// QueryScanMap
	var am, em = map[string]interface{}{}, map[string]interface{}{"id": 1, "n": 2}

	if err := s.QueryScanMap("select id, n from gockle_test.test", nil, am); err == nil {
		if !reflect.DeepEqual(am, em) {
			t.Errorf("Actual map %v, expected %v", am, em)
		}
	} else {
		t.Errorf("Actual error %v, expected no error", err)
	}

	// QueryScanMapTransaction
	am = map[string]interface{}{}

	if b, err := s.QueryScanMapTransaction("update gockle_test.test set n = 3 where id = 1 if n = 2", nil, am); err == nil {
		if !b {
			t.Error("Actual applied false, expected true")
		}

		if l := len(am); l != 0 {
			t.Errorf("Actual length %v, expected 0", l)
		}
	} else {
		t.Errorf("Actual error %v, expected no error", err)
	}

	// QuerySliceMap
	var es = []map[string]interface{}{{"id": 1, "n": 3}}

	if as, err := s.QuerySliceMap("select * from gockle_test.test"); err == nil {
		if !reflect.DeepEqual(as, es) {
			t.Errorf("Actual rows %v, expected %v", as, es)
		}
	} else {
		t.Errorf("Actual error %v, expected no error", err)
	}

	exec(tabDrop)
	exec(ksDrop)

	s.Close()
}

func init() {
	flag.Parse()

	if err := flagenv.ParseSet("gockle_", flag.CommandLine); err != nil {
		panic(err)
	}
}

func newSession(t *testing.T) Session {
	var c = gocql.NewCluster("localhost")

	c.ProtoVersion = *protoVersion
	c.Timeout = timeout

	var s, err = c.CreateSession()

	if err != nil {
		t.Skip(err)
	}

	return NewSession(s)
}

func testMock(t *testing.T, i interface{}, m *mock.Mock, tests []struct {
	method    string
	arguments []interface{}
	results   []interface{}
}) {
	var v = reflect.ValueOf(i)

	for _, test := range tests {
		t.Log("Test:", test)
		m.Reset()
		m.When(test.method, test.arguments...).Return(test.results...)

		var vs []reflect.Value

		for _, a := range test.arguments {
			vs = append(vs, reflect.ValueOf(a))
		}

		var method = v.MethodByName(test.method)

		if method.Type().IsVariadic() {
			vs = method.CallSlice(vs)
		} else {
			vs = method.Call(vs)
		}

		var is []interface{}

		for _, v := range vs {
			is = append(is, v.Interface())
		}

		if !reflect.DeepEqual(is, test.results) {
			t.Errorf("Actual %v, expected %v", is, test.results)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"runtime"

	"github.com/veandco/go-sdl2/sdl"
	"github.com/yukiisbored/Kaori/game"
)

const FPS = 60
const DELAY_TIME = 1000 / FPS

var frameStart, frameTime uint32

func main() {
	log.Println("Welcome to Kaori")
	defer log.Println("Goodbye o/")

	log.Printf("Compiled with %s for %s %s\n", runtime.Compiler, runtime.GOOS, runtime.GOARCH)

	game.Init("Kaori", sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, 800, 600, false)

	defer game.Clean()

	for game.Running() {
		frameStart = sdl.GetTicks()

		game.HandleEvents()
		game.Update()
		game.Render()

		frameTime = sdl.GetTicks() - frameStart

		if frameTime < DELAY_TIME {
			sdl.Delay(DELAY_TIME - frameTime)
		}
	}
}
<commit_msg>loader: Added Comments<commit_after>// Loader is an example of a usual Kaori Game Loader for the PC
package main

import (
	"log"
	"runtime"

	"github.com/veandco/go-sdl2/sdl"
	"github.com/yukiisbored/Kaori/game"
)

const FPS = 60                // Limit the FPS to 60
const DELAY_TIME = 1000 / FPS // How long should we delay to get 60 frames in every second

var frameStart, frameTime uint32 // When did the frame start and How long does it take to do a game update and render

func main() {
	// Show us a welomce message
	log.Println("Welcome to Kaori")

	// Don't forget to say goodbye ;)
	defer log.Println("Goodbye o/")

	// Show information about the runtime
	log.Printf("Compiled with %s for %s %s\n", runtime.Compiler, runtime.GOOS, runtime.GOARCH)

	// Don't forget to clean the game after it's done
	game.Init("Kaori", sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, 800, 600, false)

	// Don't forget to clean the game after it's done
	defer game.Clean()

	for game.Running() {
		// Get the current time to mark a frame start
		frameStart = sdl.GetTicks()

		// Run all of the 'update' functions
		game.HandleEvents()
		game.Update()
		game.Render()

		// Record the time
		frameTime = sdl.GetTicks() - frameStart

		// Check if it's faster than delay time
		if frameTime < DELAY_TIME {
			// If it is faster, delay the game to prevent "speeding" on certain tick event
			sdl.Delay(DELAY_TIME - frameTime)
		}
	}
}
<|endoftext|>


<commit_before>package smf

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"testing"
	"time"
)

func TestChunk(t *testing.T) {
	var bf bytes.Buffer
	var ch Chunk
	ch.SetType([4]byte{byte('M'), byte('T'), byte('h'), byte('d')})

	var hd Header
	hd.Format = SMF0
	hd.NumTracks = 1
	hd.TimeFormat = MetricTicks(1920)

	binary.Write(&bf, binary.BigEndian, hd.Format.Type())
	binary.Write(&bf, binary.BigEndian, hd.NumTracks)

	var tf = MetricTicks(1920)
	ticks := tf.Ticks4th()
	binary.Write(&bf, binary.BigEndian, uint16(ticks))

	ch.Write(bf.Bytes())

	var out bytes.Buffer
	ch.WriteTo(&out)

	expected := "4D 54 68 64 00 00 00 06 00 00 00 01 07 80"
	if got, want := fmt.Sprintf("% X", out.Bytes()), expected; got != want {
		t.Errorf("Chunk.Bytes() = %#v; want %#v", got, want)
	}

	ch = Chunk{}
	ch.ReadHeader(&out)

	expected = "MThd"

	if got, want := ch.Type(), expected; got != want {
		t.Errorf("Chunk.Type() = %#v; want %#v", got, want)
	}
}

func TestMetricTicks(t *testing.T) {
	var tf = MetricTicks(1920)

	expected := uint32(tf.Number()) / 2

	if got, want := tf.Ticks8th(), expected; got != want {
		t.Errorf("MetricTicks(1920).Ticks8th() ==  = %#v; want %#v", got, want)
	}

}

func TestHeaderSMF2(t *testing.T) {
	var h Header
	h.Format = SMF2
	h.NumTracks = 10
	h.TimeFormat = SMPTE30DropFrame(100)
	expected := "<Format: SMF2 (sequential tracks), NumTracks: 10, TimeFormat: SMPTE30DropFrame 100 subframes>"

	if got, want := h.String(), expected; got != want {
		t.Errorf("Header.String() = %#v; want %#v", got, want)
	}

}

func TestHeaderSMF1(t *testing.T) {
	var h Header
	h.Format = SMF1
	h.NumTracks = 10
	h.TimeFormat = SMPTE30(100)
	expected := "<Format: SMF1 (multitrack), NumTracks: 10, TimeFormat: SMPTE30 100 subframes>"

	if got, want := h.String(), expected; got != want {
		t.Errorf("Header.String() = %#v; want %#v", got, want)
	}

}

func TestHeaderSMF0(t *testing.T) {
	var h Header
	h.Format = SMF0
	h.NumTracks = 1
	h.TimeFormat = MetricTicks(0)
	expected := "<Format: SMF0 (singletrack), NumTracks: 1, TimeFormat: 960 MetricTicks>"

	if got, want := h.String(), expected; got != want {
		t.Errorf("Header.String() = %#v; want %#v", got, want)
	}

}

func TestTicksDuration(t *testing.T) {
	tests := []struct {
		resolution MetricTicks
		tempo      uint32
		deltaTicks uint32
		duration   time.Duration
	}{
		{96, 120, 96, 500 * time.Millisecond},
		{96, 120, 48, 250 * time.Millisecond},
		{96, 120, 192, 1000 * time.Millisecond},
		{90, 240, 90, 250 * time.Millisecond},
	}

	for _, test := range tests {

		if got, want := test.resolution.Duration(test.tempo, test.deltaTicks), test.duration; got != want {
			t.Errorf(
				"MetricTicks(%v).Duration(%v, %v) = %s; want %s",
				uint16(test.resolution),
				test.tempo,
				test.deltaTicks,
				got,
				want,
			)
		}

		if got, want := test.resolution.Ticks(test.tempo, test.duration), test.deltaTicks; got != want {
			t.Errorf(
				"MetricTicks(%v).Ticks(%v, %v) = %v; want %v",
				uint16(test.resolution),
				test.tempo,
				test.duration,
				got,
				want,
			)
		}
	}

}
<commit_msg>add tests for metric ticks to note length functions<commit_after>package smf

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"testing"
	"time"
)

func TestChunk(t *testing.T) {
	var bf bytes.Buffer
	var ch Chunk
	ch.SetType([4]byte{byte('M'), byte('T'), byte('h'), byte('d')})

	var hd Header
	hd.Format = SMF0
	hd.NumTracks = 1
	hd.TimeFormat = MetricTicks(1920)

	binary.Write(&bf, binary.BigEndian, hd.Format.Type())
	binary.Write(&bf, binary.BigEndian, hd.NumTracks)

	var tf = MetricTicks(1920)
	ticks := tf.Ticks4th()
	binary.Write(&bf, binary.BigEndian, uint16(ticks))

	ch.Write(bf.Bytes())

	var out bytes.Buffer
	ch.WriteTo(&out)

	expected := "4D 54 68 64 00 00 00 06 00 00 00 01 07 80"
	if got, want := fmt.Sprintf("% X", out.Bytes()), expected; got != want {
		t.Errorf("Chunk.Bytes() = %#v; want %#v", got, want)
	}

	ch = Chunk{}
	ch.ReadHeader(&out)

	expected = "MThd"

	if got, want := ch.Type(), expected; got != want {
		t.Errorf("Chunk.Type() = %#v; want %#v", got, want)
	}
}

func TestMetricTicks(t *testing.T) {
	var tf = MetricTicks(1920)

	expected := uint32(tf.Number()) / 2

	if got, want := tf.Ticks8th(), expected; got != want {
		t.Errorf("MetricTicks(1920).Ticks8th() ==  = %#v; want %#v", got, want)
	}

}

func TestHeaderSMF2(t *testing.T) {
	var h Header
	h.Format = SMF2
	h.NumTracks = 10
	h.TimeFormat = SMPTE30DropFrame(100)
	expected := "<Format: SMF2 (sequential tracks), NumTracks: 10, TimeFormat: SMPTE30DropFrame 100 subframes>"

	if got, want := h.String(), expected; got != want {
		t.Errorf("Header.String() = %#v; want %#v", got, want)
	}

}

func TestHeaderSMF1(t *testing.T) {
	var h Header
	h.Format = SMF1
	h.NumTracks = 10
	h.TimeFormat = SMPTE30(100)
	expected := "<Format: SMF1 (multitrack), NumTracks: 10, TimeFormat: SMPTE30 100 subframes>"

	if got, want := h.String(), expected; got != want {
		t.Errorf("Header.String() = %#v; want %#v", got, want)
	}

}

func TestHeaderSMF0(t *testing.T) {
	var h Header
	h.Format = SMF0
	h.NumTracks = 1
	h.TimeFormat = MetricTicks(0)
	expected := "<Format: SMF0 (singletrack), NumTracks: 1, TimeFormat: 960 MetricTicks>"

	if got, want := h.String(), expected; got != want {
		t.Errorf("Header.String() = %#v; want %#v", got, want)
	}

}

func TestTicksNoteLengths(t *testing.T) {
	var resolution MetricTicks

	tests := []struct {
		description string
		got         uint32
		expected    uint32
	}{
		{"4th", resolution.Ticks4th(), uint32(resolution.Number())},
		{"8th", resolution.Ticks8th(), 480},
		{"16th", resolution.Ticks16th(), 240},
		{"32th", resolution.Ticks32th(), 120},
		{"64th", resolution.Ticks64th(), 60},
		{"128th", resolution.Ticks128th(), 30},
		{"256th", resolution.Ticks256th(), 15},
		{"512th", resolution.Ticks512th(), 8},
		{"1024th", resolution.Ticks1024th(), 4},
	}

	for _, test := range tests {

		if got, want := test.got, test.expected; got != want {
			t.Errorf(
				"%s = %v; want %v",
				test.description,
				got,
				want,
			)
		}
	}

}

func TestTicksDuration(t *testing.T) {
	tests := []struct {
		resolution MetricTicks
		tempo      uint32
		deltaTicks uint32
		duration   time.Duration
	}{
		{96, 120, 96, 500 * time.Millisecond},
		{96, 120, 48, 250 * time.Millisecond},
		{96, 120, 192, 1000 * time.Millisecond},
		{90, 240, 90, 250 * time.Millisecond},
	}

	for _, test := range tests {

		if got, want := test.resolution.Duration(test.tempo, test.deltaTicks), test.duration; got != want {
			t.Errorf(
				"MetricTicks(%v).Duration(%v, %v) = %s; want %s",
				uint16(test.resolution),
				test.tempo,
				test.deltaTicks,
				got,
				want,
			)
		}

		if got, want := test.resolution.Ticks(test.tempo, test.duration), test.deltaTicks; got != want {
			t.Errorf(
				"MetricTicks(%v).Ticks(%v, %v) = %v; want %v",
				uint16(test.resolution),
				test.tempo,
				test.duration,
				got,
				want,
			)
		}
	}

}
<|endoftext|>


<commit_before>package storageconsul

import (
	"context"
	"net"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/caddyserver/certmagic"
	consul "github.com/hashicorp/consul/api"
	"github.com/pteich/errors"
	"go.uber.org/zap"
)

// ConsulStorage allows to store certificates and other TLS resources
// in a shared cluster environment using Consul's key/value-store.
// It uses distributed locks to ensure consistency.
type ConsulStorage struct {
	certmagic.Storage
	ConsulClient *consul.Client
	logger       *zap.SugaredLogger
	muLocks      sync.RWMutex
	locks        map[string]*consul.Lock

	Address     string `json:"address"`
	Token       string `json:"token"`
	Timeout     int    `json:"timeout"`
	Prefix      string `json:"prefix"`
	ValuePrefix string `json:"value_prefix"`
	AESKey      []byte `json:"aes_key"`
	TlsEnabled  bool   `json:"tls_enabled"`
	TlsInsecure bool   `json:"tls_insecure"`
}

// New connects to Consul and returns a ConsulStorage
func New() *ConsulStorage {
	// create ConsulStorage and pre-set values
	s := ConsulStorage{
		locks:       make(map[string]*consul.Lock),
		AESKey:      []byte(DefaultAESKey),
		ValuePrefix: DefaultValuePrefix,
		Prefix:      DefaultPrefix,
		Timeout:     DefaultTimeout,
	}

	return &s
}

func (cs *ConsulStorage) prefixKey(key string) string {
	return path.Join(cs.Prefix, key)
}

// Lock acquires a distributed lock for the given key or blocks until it gets one
func (cs *ConsulStorage) Lock(ctx context.Context, key string) error {
	cs.logger.Debugf("trying lock for %s", key)

	if _, isLocked := cs.GetLock(key); isLocked {
		return nil
	}

	// prepare the distributed lock
	cs.logger.Infof("creating Consul lock for %s", key)
	lock, err := cs.ConsulClient.LockOpts(&consul.LockOptions{
		Key:          cs.prefixKey(key),
		LockWaitTime: time.Duration(cs.Timeout) * time.Second,
		LockTryOnce:  true,
	})
	if err != nil {
		return errors.Wrapf(err, "could not create lock for %s", cs.prefixKey(key))
	}

	// acquire the lock and return a channel that is closed upon lost
	lockActive, err := lock.Lock(ctx.Done())
	if err != nil {
		return errors.Wrapf(err, "unable to lock %s", cs.prefixKey(key))
	}

	// auto-unlock and clean list of locks in case of lost
	go func() {
		<-lockActive
		cs.Unlock(key)
	}()

	// save the lock
	cs.muLocks.Lock()
	cs.locks[key] = lock
	cs.muLocks.Unlock()

	return nil
}

func (cs *ConsulStorage) GetLock(key string) (*consul.Lock, bool) {
	cs.muLocks.RLock()
	defer cs.muLocks.RUnlock()

	// if we already hold the lock, return early
	if lock, exists := cs.locks[key]; exists {
		return lock, true
	}

	return nil, false
}

// Unlock releases a specific lock
func (cs *ConsulStorage) Unlock(key string) error {
	// check if we own it and unlock
	lock, exists := cs.GetLock(key)
	if !exists {
		return errors.Errorf("lock %s not found", cs.prefixKey(key))
	}

	err := lock.Unlock()
	if err != nil {
		return errors.Wrapf(err, "unable to unlock %s", cs.prefixKey(key))
	}

	cs.muLocks.Lock()
	delete(cs.locks, key)
	cs.muLocks.Unlock()

	return nil
}

// Store saves encrypted data value for a key in Consul KV
func (cs ConsulStorage) Store(key string, value []byte) error {
	kv := &consul.KVPair{Key: cs.prefixKey(key)}

	// prepare the stored data
	consulData := &StorageData{
		Value:    value,
		Modified: time.Now(),
	}

	encryptedValue, err := cs.EncryptStorageData(consulData)
	if err != nil {
		return errors.Wrapf(err, "unable to encode data for %s", cs.prefixKey(key))
	}

	kv.Value = encryptedValue

	if _, err = cs.ConsulClient.KV().Put(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to store data for %s", cs.prefixKey(key))
	}

	return nil
}

// Load retrieves the value for a key from Consul KV
func (cs ConsulStorage) Load(key string) ([]byte, error) {
	cs.logger.Debugf("loading data from Consul for %s", key)

	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return nil, errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return nil, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to decrypt data for %s", cs.prefixKey(key))
	}

	return contents.Value, nil
}

// Delete a key from Consul KV
func (cs ConsulStorage) Delete(key string) error {
	cs.logger.Infof("deleting key %s from Consul", key)

	// first obtain existing keypair
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.ErrNotExist(err)
	}

	// no do a Check-And-Set operation to verify we really deleted the key
	if success, _, err := cs.ConsulClient.KV().DeleteCAS(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to delete data for %s", cs.prefixKey(key))
	} else if !success {
		return errors.Errorf("failed to lock data delete for %s", cs.prefixKey(key))
	}

	return nil
}

// Exists checks if a key exists
func (cs ConsulStorage) Exists(key string) bool {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if kv != nil && err == nil {
		return true
	}
	return false
}

// List returns a list with all keys under a given prefix
func (cs ConsulStorage) List(prefix string, recursive bool) ([]string, error) {
	var keysFound []string

	// get a list of all keys at prefix
	keys, _, err := cs.ConsulClient.KV().Keys(cs.prefixKey(prefix), "", &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return keysFound, err
	}

	if len(keys) == 0 {
		return keysFound, certmagic.ErrNotExist(errors.Errorf("no keys at %s", prefix))
	}

	// remove default prefix from keys
	for _, key := range keys {
		if strings.HasPrefix(key, cs.prefixKey(prefix)) {
			key = strings.TrimPrefix(key, cs.Prefix+"/")
			keysFound = append(keysFound, key)
		}
	}

	// if recursive wanted, just return all keys
	if recursive {
		return keysFound, nil
	}

	// for non-recursive split path and look for unique keys just under given prefix
	keysMap := make(map[string]bool)
	for _, key := range keysFound {
		dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
		keysMap[dir[0]] = true
	}

	keysFound = make([]string, 0)
	for key := range keysMap {
		keysFound = append(keysFound, path.Join(prefix, key))
	}

	return keysFound, nil
}

// Stat returns statistic data of a key
func (cs ConsulStorage) Stat(key string) (certmagic.KeyInfo, error) {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.KeyInfo{}, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to decrypt data for %s", cs.prefixKey(key))
	}

	return certmagic.KeyInfo{
		Key:        key,
		Modified:   contents.Modified,
		Size:       int64(len(contents.Value)),
		IsTerminal: false,
	}, nil
}

func (cs *ConsulStorage) createConsulClient() error {
	// get the default config
	consulCfg := consul.DefaultConfig()
	if cs.Address != "" {
		consulCfg.Address = cs.Address
	}
	if cs.Token != "" {
		consulCfg.Token = cs.Token
	}
	if cs.TlsEnabled {
		consulCfg.Scheme = "https"
	}
	consulCfg.TLSConfig.InsecureSkipVerify = cs.TlsInsecure

	// set a dial context to prevent default keepalive
	consulCfg.Transport.DialContext = (&net.Dialer{
		Timeout:   time.Duration(cs.Timeout) * time.Second,
		KeepAlive: time.Duration(cs.Timeout) * time.Second,
	}).DialContext

	// create the Consul API client
	consulClient, err := consul.NewClient(consulCfg)
	if err != nil {
		return errors.Wrap(err, "unable to create Consul client")
	}
	if _, err := consulClient.Agent().NodeName(); err != nil {
		return errors.Wrap(err, "unable to ping Consul")
	}

	cs.ConsulClient = consulClient
	return nil
}
<commit_msg>Prevent mutex leak<commit_after>package storageconsul

import (
	"context"
	"net"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/caddyserver/certmagic"
	consul "github.com/hashicorp/consul/api"
	"github.com/pteich/errors"
	"go.uber.org/zap"
)

// ConsulStorage allows to store certificates and other TLS resources
// in a shared cluster environment using Consul's key/value-store.
// It uses distributed locks to ensure consistency.
type ConsulStorage struct {
	certmagic.Storage
	ConsulClient *consul.Client
	logger       *zap.SugaredLogger
	muLocks      *sync.RWMutex
	locks        map[string]*consul.Lock

	Address     string `json:"address"`
	Token       string `json:"token"`
	Timeout     int    `json:"timeout"`
	Prefix      string `json:"prefix"`
	ValuePrefix string `json:"value_prefix"`
	AESKey      []byte `json:"aes_key"`
	TlsEnabled  bool   `json:"tls_enabled"`
	TlsInsecure bool   `json:"tls_insecure"`
}

// New connects to Consul and returns a ConsulStorage
func New() *ConsulStorage {
	// create ConsulStorage and pre-set values
	s := ConsulStorage{
		locks:       make(map[string]*consul.Lock),
		AESKey:      []byte(DefaultAESKey),
		ValuePrefix: DefaultValuePrefix,
		Prefix:      DefaultPrefix,
		Timeout:     DefaultTimeout,
		muLocks:     &sync.RWMutex{},
	}

	return &s
}

func (cs *ConsulStorage) prefixKey(key string) string {
	return path.Join(cs.Prefix, key)
}

// Lock acquires a distributed lock for the given key or blocks until it gets one
func (cs *ConsulStorage) Lock(ctx context.Context, key string) error {
	cs.logger.Debugf("trying lock for %s", key)

	if _, isLocked := cs.GetLock(key); isLocked {
		return nil
	}

	// prepare the distributed lock
	cs.logger.Infof("creating Consul lock for %s", key)
	lock, err := cs.ConsulClient.LockOpts(&consul.LockOptions{
		Key:          cs.prefixKey(key),
		LockWaitTime: time.Duration(cs.Timeout) * time.Second,
		LockTryOnce:  true,
	})
	if err != nil {
		return errors.Wrapf(err, "could not create lock for %s", cs.prefixKey(key))
	}

	// acquire the lock and return a channel that is closed upon lost
	lockActive, err := lock.Lock(ctx.Done())
	if err != nil {
		return errors.Wrapf(err, "unable to lock %s", cs.prefixKey(key))
	}

	// auto-unlock and clean list of locks in case of lost
	go func() {
		<-lockActive
		cs.Unlock(key)
	}()

	// save the lock
	cs.muLocks.Lock()
	cs.locks[key] = lock
	cs.muLocks.Unlock()

	return nil
}

func (cs *ConsulStorage) GetLock(key string) (*consul.Lock, bool) {
	cs.muLocks.RLock()
	defer cs.muLocks.RUnlock()

	// if we already hold the lock, return early
	if lock, exists := cs.locks[key]; exists {
		return lock, true
	}

	return nil, false
}

// Unlock releases a specific lock
func (cs *ConsulStorage) Unlock(key string) error {
	// check if we own it and unlock
	lock, exists := cs.GetLock(key)
	if !exists {
		return errors.Errorf("lock %s not found", cs.prefixKey(key))
	}

	err := lock.Unlock()
	if err != nil {
		return errors.Wrapf(err, "unable to unlock %s", cs.prefixKey(key))
	}

	cs.muLocks.Lock()
	delete(cs.locks, key)
	cs.muLocks.Unlock()

	return nil
}

// Store saves encrypted data value for a key in Consul KV
func (cs ConsulStorage) Store(key string, value []byte) error {
	kv := &consul.KVPair{Key: cs.prefixKey(key)}

	// prepare the stored data
	consulData := &StorageData{
		Value:    value,
		Modified: time.Now(),
	}

	encryptedValue, err := cs.EncryptStorageData(consulData)
	if err != nil {
		return errors.Wrapf(err, "unable to encode data for %s", cs.prefixKey(key))
	}

	kv.Value = encryptedValue

	if _, err = cs.ConsulClient.KV().Put(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to store data for %s", cs.prefixKey(key))
	}

	return nil
}

// Load retrieves the value for a key from Consul KV
func (cs ConsulStorage) Load(key string) ([]byte, error) {
	cs.logger.Debugf("loading data from Consul for %s", key)

	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return nil, errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return nil, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to decrypt data for %s", cs.prefixKey(key))
	}

	return contents.Value, nil
}

// Delete a key from Consul KV
func (cs ConsulStorage) Delete(key string) error {
	cs.logger.Infof("deleting key %s from Consul", key)

	// first obtain existing keypair
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.ErrNotExist(err)
	}

	// no do a Check-And-Set operation to verify we really deleted the key
	if success, _, err := cs.ConsulClient.KV().DeleteCAS(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to delete data for %s", cs.prefixKey(key))
	} else if !success {
		return errors.Errorf("failed to lock data delete for %s", cs.prefixKey(key))
	}

	return nil
}

// Exists checks if a key exists
func (cs ConsulStorage) Exists(key string) bool {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if kv != nil && err == nil {
		return true
	}
	return false
}

// List returns a list with all keys under a given prefix
func (cs ConsulStorage) List(prefix string, recursive bool) ([]string, error) {
	var keysFound []string

	// get a list of all keys at prefix
	keys, _, err := cs.ConsulClient.KV().Keys(cs.prefixKey(prefix), "", &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return keysFound, err
	}

	if len(keys) == 0 {
		return keysFound, certmagic.ErrNotExist(errors.Errorf("no keys at %s", prefix))
	}

	// remove default prefix from keys
	for _, key := range keys {
		if strings.HasPrefix(key, cs.prefixKey(prefix)) {
			key = strings.TrimPrefix(key, cs.Prefix+"/")
			keysFound = append(keysFound, key)
		}
	}

	// if recursive wanted, just return all keys
	if recursive {
		return keysFound, nil
	}

	// for non-recursive split path and look for unique keys just under given prefix
	keysMap := make(map[string]bool)
	for _, key := range keysFound {
		dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
		keysMap[dir[0]] = true
	}

	keysFound = make([]string, 0)
	for key := range keysMap {
		keysFound = append(keysFound, path.Join(prefix, key))
	}

	return keysFound, nil
}

// Stat returns statistic data of a key
func (cs ConsulStorage) Stat(key string) (certmagic.KeyInfo, error) {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.KeyInfo{}, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to decrypt data for %s", cs.prefixKey(key))
	}

	return certmagic.KeyInfo{
		Key:        key,
		Modified:   contents.Modified,
		Size:       int64(len(contents.Value)),
		IsTerminal: false,
	}, nil
}

func (cs *ConsulStorage) createConsulClient() error {
	// get the default config
	consulCfg := consul.DefaultConfig()
	if cs.Address != "" {
		consulCfg.Address = cs.Address
	}
	if cs.Token != "" {
		consulCfg.Token = cs.Token
	}
	if cs.TlsEnabled {
		consulCfg.Scheme = "https"
	}
	consulCfg.TLSConfig.InsecureSkipVerify = cs.TlsInsecure

	// set a dial context to prevent default keepalive
	consulCfg.Transport.DialContext = (&net.Dialer{
		Timeout:   time.Duration(cs.Timeout) * time.Second,
		KeepAlive: time.Duration(cs.Timeout) * time.Second,
	}).DialContext

	// create the Consul API client
	consulClient, err := consul.NewClient(consulCfg)
	if err != nil {
		return errors.Wrap(err, "unable to create Consul client")
	}
	if _, err := consulClient.Agent().NodeName(); err != nil {
		return errors.Wrap(err, "unable to ping Consul")
	}

	cs.ConsulClient = consulClient
	return nil
}
<|endoftext|>


<commit_before>// Package storage provides types and functionality for abstracting storage systems (Local, in memory, S3, Google Cloud
// storage) into a common interface.
package storage

import (
	"fmt"
	"io"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// File contains the metadata required to define a file (for reading).
type File struct {
	io.ReadCloser           // Underlying data.
	Name          string    // Name of the file (likely basename).
	ModTime       time.Time // Modified time of the file.
	Size          int64     // Size of the file.
}

// isNotExister is an interface used to define the behaviour of errors resulting
// from operations which report missing files/paths.
type isNotExister interface {
	isNotExist() bool
}

// IsNotExist returns a boolean indicating whether the error is known to report that
// a path.
func IsNotExist(err error) bool {
	e, ok := err.(isNotExister)
	return ok && e.isNotExist()
}

// notExistError is returned from FS.Open implementations when a requested
// path does not exist.
type notExistError struct {
	Path string
}

func (e *notExistError) isNotExist() bool { return true }

// Error implements error
func (e *notExistError) Error() string {
	return fmt.Sprintf("storage %v: file does not exist", e.Path)
}

// FS is an interface which defines a virtual filesystem.
type FS interface {
	Walker

	// Open opens an existing file in the filesystem.
	Open(ctx context.Context, path string) (*File, error)

	// Create makes a new file in the filesystem.  Callers must close the
	// returned WriteCloser and check the error to be sure that the file
	// was successfully written.
	Create(ctx context.Context, path string) (io.WriteCloser, error)

	// Delete removes a file from the filesystem.
	Delete(ctx context.Context, path string) error
}

// FSFromURL takes a file system path and returns a FSWalker
// corresponding to a supported storage system (CloudStorage,
// S3, or Local if no platform-specific prefix is used).
func FSFromURL(path string) FS {
	if strings.HasPrefix(path, "gs://") {
		return &CloudStorage{Bucket: strings.TrimPrefix(path, "gs://")}
	}
	if strings.HasPrefix(path, "s3://") {
		return &S3{Bucket: strings.TrimPrefix(path, "s3://")}
	}
	return Local(path)
}

// PrefixFS creates a FS which wraps fs and prefixes all paths with prefix.
func PrefixFS(fs FS, prefix string) FS {
	return prefixFS{
		prefix: prefix,
		fs:     fs,
	}
}

type prefixFS struct {
	prefix string
	fs     FS
}

func (pfs prefixFS) addPrefix(path string) string {
	return fmt.Sprintf("%v%v", pfs.prefix, path)
}

// Open implements FS.
func (pfs prefixFS) Open(ctx context.Context, path string) (*File, error) {
	return pfs.fs.Open(ctx, pfs.addPrefix(path))
}

// Create implements FS.
func (pfs prefixFS) Create(ctx context.Context, path string) (io.WriteCloser, error) {
	return pfs.fs.Create(ctx, pfs.addPrefix(path))
}

// Delete implements FS.
func (pfs prefixFS) Delete(ctx context.Context, path string) error {
	return pfs.fs.Delete(ctx, pfs.addPrefix(path))
}

// Walk implements FS.
func (pfs prefixFS) Walk(ctx context.Context, path string, fn WalkFn) error {
	return pfs.fs.Walk(ctx, pfs.addPrefix(path), func(path string) error {
		path = strings.TrimPrefix(path, pfs.prefix)
		return fn(path)
	})
}
<commit_msg>Refactor PrefixFS -> Prefix.<commit_after>// Package storage provides types and functionality for abstracting storage systems (Local, in memory, S3, Google Cloud
// storage) into a common interface.
package storage

import (
	"fmt"
	"io"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// File contains the metadata required to define a file (for reading).
type File struct {
	io.ReadCloser           // Underlying data.
	Name          string    // Name of the file (likely basename).
	ModTime       time.Time // Modified time of the file.
	Size          int64     // Size of the file.
}

// isNotExister is an interface used to define the behaviour of errors resulting
// from operations which report missing files/paths.
type isNotExister interface {
	isNotExist() bool
}

// IsNotExist returns a boolean indicating whether the error is known to report that
// a path.
func IsNotExist(err error) bool {
	e, ok := err.(isNotExister)
	return ok && e.isNotExist()
}

// notExistError is returned from FS.Open implementations when a requested
// path does not exist.
type notExistError struct {
	Path string
}

func (e *notExistError) isNotExist() bool { return true }

// Error implements error
func (e *notExistError) Error() string {
	return fmt.Sprintf("storage %v: file does not exist", e.Path)
}

// FS is an interface which defines a virtual filesystem.
type FS interface {
	Walker

	// Open opens an existing file in the filesystem.
	Open(ctx context.Context, path string) (*File, error)

	// Create makes a new file in the filesystem.  Callers must close the
	// returned WriteCloser and check the error to be sure that the file
	// was successfully written.
	Create(ctx context.Context, path string) (io.WriteCloser, error)

	// Delete removes a file from the filesystem.
	Delete(ctx context.Context, path string) error
}

// FSFromURL takes a file system path and returns a FSWalker
// corresponding to a supported storage system (CloudStorage,
// S3, or Local if no platform-specific prefix is used).
func FSFromURL(path string) FS {
	if strings.HasPrefix(path, "gs://") {
		return &CloudStorage{Bucket: strings.TrimPrefix(path, "gs://")}
	}
	if strings.HasPrefix(path, "s3://") {
		return &S3{Bucket: strings.TrimPrefix(path, "s3://")}
	}
	return Local(path)
}

// Prefix creates a FS which wraps fs and prefixes all paths with prefix.
func Prefix(fs FS, prefix string) FS {
	return pfx{
		fs:     fs,
		prefix: prefix,
	}
}

type pfx struct {
	fs     FS
	prefix string
}

func (p pfx) addPrefix(path string) string {
	return fmt.Sprintf("%v%v", p.prefix, path)
}

// Open implements FS.
func (p pfx) Open(ctx context.Context, path string) (*File, error) {
	return p.fs.Open(ctx, p.addPrefix(path))
}

// Create implements FS.
func (p pfx) Create(ctx context.Context, path string) (io.WriteCloser, error) {
	return p.fs.Create(ctx, p.addPrefix(path))
}

// Delete implements FS.
func (p pfx) Delete(ctx context.Context, path string) error {
	return p.fs.Delete(ctx, p.addPrefix(path))
}

// Walk implements FS.
func (p pfx) Walk(ctx context.Context, path string, fn WalkFn) error {
	return p.fs.Walk(ctx, p.addPrefix(path), func(path string) error {
		path = strings.TrimPrefix(path, p.prefix)
		return fn(path)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/go-sqlite/go1/sqlite3"
	"code.google.com/p/gopacket"
	"fmt"
	"net/http"
	"time"
)

/* Storage
 *
 * This implements the persistence layer, storing all trace data (connections,
 * requests, responses). This layer does nothing more than storing data,
 * reporting happens offline.
 */

type Storage struct {
	c         *sqlite3.Conn
	insConn   *sqlite3.Stmt
	closeConn *sqlite3.Stmt
	insReq    *sqlite3.Stmt
	insResp   *sqlite3.Stmt
}

// constructor
func NewStorage() (*Storage, error) {
	var storage *Storage

	/* Create a new temporary SQLite database */
	c, err := sqlite3.Open("trace.db")
	if err != nil {
		return nil, err
	}

	storage = &Storage{c, nil, nil, nil, nil}

	c.Exec("CREATE TABLE conns(id INTEGER, opentimestamp INTEGER, " +
		"closetimestamp INTEGER, src_ip TEXT, src_port INTEGER, " +
		"dst_ip string, dst_port INTEGER)")
	storage.insConn, err = c.Prepare("INSERT into conns VALUES (?, ?, 0, ?, ?, ?, ?)")
	if err != nil {
		return nil, err
	}
	storage.closeConn, err = c.Prepare("UPDATE conns SET closetimestamp = ? " +
		"where id = ?")
	if err != nil {
		return nil, err
	}

	c.Exec("CREATE TABLE reqresps(connID INTEGER, reqtimestamp INTEGER, reqId INTEGER," +
		"method TEXT, url TEXT, resptimestamp INTEGER, status INTEGER)")
	storage.insReq, err = c.Prepare("INSERT into reqresps VALUES (" +
		"?, ?, ?, ?, ?, 0, 0)")
	if err != nil {
		return nil, err
	}
	storage.insResp, err = c.Prepare("UPDATE reqresps set resptimestamp = ?, " +
		"status = ? WHERE reqID = ?")
	if err != nil {
		return nil, err
	}

	return storage, err
}

func (s *Storage) PacketInScope(gopacket.Packet) bool {
	return true
}

/* TODO: fix ports */
func (s *Storage) OpenTCPConnection(connID uint64, timestamp time.Time) error {
	/* SQLite doesn't support uint64, convert to int64 */
	return s.insConn.Exec(int64(connID), timestamp.UnixNano(), "",
		2, "", 3)
}

func (s *Storage) SentRequest(connID uint64, reqID int64, timestamp time.Time,
	req *http.Request) error {
	return s.insReq.Exec(int64(connID), timestamp.UnixNano(), reqID,
		req.Method, req.URL.String())
}

func (s *Storage) ReceivedResponse(connID uint64, reqID int64, timestamp time.Time,
	resp *http.Response) error {
	return s.insResp.Exec(timestamp.UnixNano(), resp.StatusCode, reqID)
}

func (s *Storage) avgWaitingTime(connID int64) (float64, error) {
	var stmt *sqlite3.Stmt
	var err error

	var avgWaitingTimeNS float64
	sql := "SELECT AVG(resptimestamp - reqtimestamp) FROM reqresps WHERE connID=? " +
		" AND status != 0"
	if stmt, err = s.c.Query(sql, connID); err != nil {
		return 0.0, err
	}
	stmt.Scan(&avgWaitingTimeNS)

	return avgWaitingTimeNS, nil
}

func (s *Storage) arrivalRate(connID int64) (float64, error) {
	var stmt *sqlite3.Stmt
	var err error

	var nrOfRequests int64
	sql := "SELECT count(*) FROM reqresps WHERE connID=?"
	if stmt, err = s.c.Query(sql, connID); err != nil {
		return 0.0, err
	}
	stmt.Scan(&nrOfRequests)

	var minReqTS, maxReqTS int64
	sql = "SELECT MIN(reqtimestamp), MAX(reqtimestamp) from reqresps WHERE connID=?"
	if stmt, err = s.c.Query(sql, connID); err != nil {
		return 0.0, err
	}
	stmt.Scan(&minReqTS, &maxReqTS)
	reqTimeRangeNS := maxReqTS - minReqTS

	arrivalRatePerNS := float64(nrOfRequests) / float64(reqTimeRangeNS)
	return arrivalRatePerNS, nil
}

func (s *Storage) avgQueueLength(connID int64) (float64, error) {

	var err error

	/* Arrival rate */
	var arrivalRatePerNS float64
	if arrivalRatePerNS, err = s.arrivalRate(connID); err != nil {
		return 0.0, err
	}

	/* Arrivage waiting time */
	var avgWaitingTimeNS float64
	if avgWaitingTimeNS, err = s.avgWaitingTime(connID); err != nil {
		return 0.0, err
	}

	avgQueueSize := arrivalRatePerNS * avgWaitingTimeNS
	return avgQueueSize, nil
}

/* TODO: This function will probably need to move to another layer. */
func (s *Storage) Report() error {
	fmt.Println()

	var stmt *sqlite3.Stmt
	sql := "SELECT id, opentimestamp FROM conns"
	i := 0

	fmt.Printf("Conn\t# reqs\t# noresp  avg queue  per method\t\n")
	for connstmt, err := s.c.Query(sql); err == nil; err = connstmt.Next() {
		i++

		var connID int64
		var nsec int64
		connstmt.Scan(&connID, &nsec)

		fmt.Printf("%4d\t", i)
		var nrOfRequests int64
		sql = "SELECT count(*) FROM reqresps WHERE connID=?"
		if stmt, err = s.c.Query(sql, connID); err != nil {
			return err
		}
		stmt.Scan(&nrOfRequests)
		fmt.Printf("%6d\t", nrOfRequests)

		var nrOfRequestsNoResp int64
		sql = "SELECT count(*) FROM reqresps where status = 0 AND connID=?"
		if stmt, err = s.c.Query(sql, connID); err != nil {
			return err
		}
		stmt.Scan(&nrOfRequestsNoResp)
		fmt.Printf("%8d  ", nrOfRequestsNoResp)

		var avgQueueLength float64
		if avgQueueLength, err = s.avgQueueLength(connID); err != nil {
			return err
		}
		fmt.Printf("%9.1f  ", avgQueueLength)

		sql = "SELECT method, count(method) FROM reqresps WHERE connID=? GROUP BY method " +
			" ORDER BY reqtimestamp "
		for stmt, err = s.c.Query(sql, connID); err == nil; err = stmt.Next() {
			var method string
			var nrOfRequestsPerMethod int64
			stmt.Scan(&method, &nrOfRequestsPerMethod)
			fmt.Printf("%s:%d ", method, nrOfRequestsPerMethod)
		}

		fmt.Printf("\t\n")
	}

	/* Average response time for each request */
	sql = "SELECT "
	return nil
}
<commit_msg>Fix +Inf bug when calculating avg queue length in case only one request  is send over a connection. Fix typo.<commit_after>package main

import (
	"code.google.com/p/go-sqlite/go1/sqlite3"
	"code.google.com/p/gopacket"
	"fmt"
	"net/http"
	"time"
)

/* Storage
 *
 * This implements the persistence layer, storing all trace data (connections,
 * requests, responses). This layer does nothing more than storing data,
 * reporting happens offline.
 */

type Storage struct {
	c         *sqlite3.Conn
	insConn   *sqlite3.Stmt
	closeConn *sqlite3.Stmt
	insReq    *sqlite3.Stmt
	insResp   *sqlite3.Stmt
}

// constructor
func NewStorage() (*Storage, error) {
	var storage *Storage

	/* Create a new temporary SQLite database */
	c, err := sqlite3.Open("trace.db")
	if err != nil {
		return nil, err
	}

	storage = &Storage{c, nil, nil, nil, nil}

	c.Exec("CREATE TABLE conns(id INTEGER, opentimestamp INTEGER, " +
		"closetimestamp INTEGER, src_ip TEXT, src_port INTEGER, " +
		"dst_ip string, dst_port INTEGER)")
	storage.insConn, err = c.Prepare("INSERT into conns VALUES (?, ?, 0, ?, ?, ?, ?)")
	if err != nil {
		return nil, err
	}
	storage.closeConn, err = c.Prepare("UPDATE conns SET closetimestamp = ? " +
		"where id = ?")
	if err != nil {
		return nil, err
	}

	c.Exec("CREATE TABLE reqresps(connID INTEGER, reqtimestamp INTEGER, reqId INTEGER," +
		"method TEXT, url TEXT, resptimestamp INTEGER, status INTEGER)")
	storage.insReq, err = c.Prepare("INSERT into reqresps VALUES (" +
		"?, ?, ?, ?, ?, 0, 0)")
	if err != nil {
		return nil, err
	}
	storage.insResp, err = c.Prepare("UPDATE reqresps set resptimestamp = ?, " +
		"status = ? WHERE reqID = ?")
	if err != nil {
		return nil, err
	}

	return storage, err
}

func (s *Storage) PacketInScope(gopacket.Packet) bool {
	return true
}

/* TODO: fix ports */
func (s *Storage) OpenTCPConnection(connID uint64, timestamp time.Time) error {
	/* SQLite doesn't support uint64, convert to int64 */
	return s.insConn.Exec(int64(connID), timestamp.UnixNano(), "",
		2, "", 3)
}

func (s *Storage) SentRequest(connID uint64, reqID int64, timestamp time.Time,
	req *http.Request) error {
	return s.insReq.Exec(int64(connID), timestamp.UnixNano(), reqID,
		req.Method, req.URL.String())
}

func (s *Storage) ReceivedResponse(connID uint64, reqID int64, timestamp time.Time,
	resp *http.Response) error {
	return s.insResp.Exec(timestamp.UnixNano(), resp.StatusCode, reqID)
}

func (s *Storage) avgWaitingTime(connID int64) (float64, error) {
	var stmt *sqlite3.Stmt
	var err error

	var avgWaitingTimeNS float64
	sql := "SELECT AVG(resptimestamp - reqtimestamp) FROM reqresps WHERE connID=? " +
		" AND status != 0"
	if stmt, err = s.c.Query(sql, connID); err != nil {
		return 0.0, err
	}
	stmt.Scan(&avgWaitingTimeNS)

	return avgWaitingTimeNS, nil
}

func (s *Storage) arrivalRate(connID int64) (float64, error) {
	var stmt *sqlite3.Stmt
	var err error

	var nrOfRequests int64
	sql := "SELECT count(*) FROM reqresps WHERE connID=?"
	if stmt, err = s.c.Query(sql, connID); err != nil {
		return 0.0, err
	}
	stmt.Scan(&nrOfRequests)

	var minReqTS, maxReqTS int64
	sql = "SELECT MIN(reqtimestamp), MAX(reqtimestamp) from reqresps WHERE connID=?"
	if stmt, err = s.c.Query(sql, connID); err != nil {
		return 0.0, err
	}
	stmt.Scan(&minReqTS, &maxReqTS)
	reqTimeRangeNS := maxReqTS - minReqTS

	/* If there was only one request, set the arrival rate to 0. */
	if reqTimeRangeNS == 0 {
		return 0.0, nil
	}

	arrivalRatePerNS := float64(nrOfRequests) / float64(reqTimeRangeNS)
	return arrivalRatePerNS, nil
}

func (s *Storage) avgQueueLength(connID int64) (float64, error) {

	var err error

	/* Arrival rate */
	var arrivalRatePerNS float64
	if arrivalRatePerNS, err = s.arrivalRate(connID); err != nil {
		return 0.0, err
	}

	/* Average waiting time */
	var avgWaitingTimeNS float64
	if avgWaitingTimeNS, err = s.avgWaitingTime(connID); err != nil {
		return 0.0, err
	}

	avgQueueSize := arrivalRatePerNS * avgWaitingTimeNS
	return avgQueueSize, nil
}

/* TODO: This function will probably need to move to another layer. */
func (s *Storage) Report() error {
	fmt.Println()

	var stmt *sqlite3.Stmt
	sql := "SELECT id, opentimestamp FROM conns"
	i := 0

	fmt.Printf("Conn\t# reqs\t# noresp  avg queue  per method\t\n")
	for connstmt, err := s.c.Query(sql); err == nil; err = connstmt.Next() {
		i++

		var connID int64
		var nsec int64
		connstmt.Scan(&connID, &nsec)

		fmt.Printf("%4d\t", i)
		var nrOfRequests int64
		sql = "SELECT count(*) FROM reqresps WHERE connID=?"
		if stmt, err = s.c.Query(sql, connID); err != nil {
			return err
		}
		stmt.Scan(&nrOfRequests)
		fmt.Printf("%6d\t", nrOfRequests)

		var nrOfRequestsNoResp int64
		sql = "SELECT count(*) FROM reqresps where status = 0 AND connID=?"
		if stmt, err = s.c.Query(sql, connID); err != nil {
			return err
		}
		stmt.Scan(&nrOfRequestsNoResp)
		fmt.Printf("%8d  ", nrOfRequestsNoResp)

		var avgQueueLength float64
		if avgQueueLength, err = s.avgQueueLength(connID); err != nil {
			return err
		}
		fmt.Printf("%9.1f  ", avgQueueLength)

		sql = "SELECT method, count(method) FROM reqresps WHERE connID=? GROUP BY method " +
			" ORDER BY reqtimestamp "
		for stmt, err = s.c.Query(sql, connID); err == nil; err = stmt.Next() {
			var method string
			var nrOfRequestsPerMethod int64
			stmt.Scan(&method, &nrOfRequestsPerMethod)
			fmt.Printf("%s:%d ", method, nrOfRequestsPerMethod)
		}

		fmt.Printf("\t\n")
	}

	/* Average response time for each request */
	sql = "SELECT "
	return nil
}
<|endoftext|>