<commit_before>/*
 *
 * Copyright 2016, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// Package stats is for collecting and reporting various network and RPC stats.
// This package is for monitoring purpose only.
// All APIs are experimental.
package stats // import "google.golang.org/grpc/stats"

import (
	"net"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc/grpclog"
)

// RPCStats contains stats information about RPCs.
// All stats types in this package implements this interface.
type RPCStats interface {
	isStats()
	// IsClient indicates if the stats is a client stats.
	IsClient() bool
}

// InPayload contains the information for a incoming payload.
type InPayload struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// Payload is the payload with original type.
	Payload interface{}
	// Data is the unencrypted message payload.
	Data []byte
	// Length is the length of uncompressed data.
	Length int
	// WireLength is the length of data on wire (compressed, signed, encrypted).
	WireLength int
	// RecvTime is the time when the payload is received.
	RecvTime time.Time
}

func (s *InPayload) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *InPayload) IsClient() bool { return s.Client }

// InHeader indicates a header is received.
// Method, addresses and Encryption are only valid if Client is false.
type InHeader struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int

	// Method is the full RPC method string, i.e., /package.service/method.
	Method string
	// RemoteAddr is the remote address of the corresponding connection.
	RemoteAddr net.Addr
	// LocalAddr is the local address of the corresponding connection.
	LocalAddr net.Addr
	// Encryption is encrypt method used in the RPC.
	Encryption string
}

func (s *InHeader) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *InHeader) IsClient() bool { return s.Client }

// InTrailer indicates a trailer is received.
type InTrailer struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int
}

func (s *InTrailer) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *InTrailer) IsClient() bool { return s.Client }

// OutPayload contains the information for a outgoing payload.
type OutPayload struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// Payload is the payload with original type.
	Payload interface{}
	// Data is the unencrypted message payload.
	Data []byte
	// Length is the length of uncompressed data.
	Length int
	// WireLength is the length of data on wire (compressed, signed, encrypted).
	WireLength int
	// SentTime is the time when the payload is sent.
	SentTime time.Time
}

func (s *OutPayload) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *OutPayload) IsClient() bool { return s.Client }

// OutHeader indicates a header is sent.
// Method, addresses and Encryption are only valid if Client is true.
type OutHeader struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int

	// Method is the full RPC method string, i.e., /package.service/method.
	Method string
	// RemoteAddr is the remote address of the corresponding connection.
	RemoteAddr net.Addr
	// LocalAddr is the local address of the corresponding connection.
	LocalAddr net.Addr
	// Encryption is encrypt method used in the RPC.
	Encryption string
	// Failfast indicates if this RPC is failfast.
	FailFast bool
}

func (s *OutHeader) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *OutHeader) IsClient() bool { return s.Client }

// OutTrailer indicates a trailer is sent.
type OutTrailer struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int
}

func (s *OutTrailer) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *OutTrailer) IsClient() bool { return s.Client }

// RPCErr indicates an error happens.
type RPCErr struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// Error is the error just happened. Its type is gRPC error.
	Error error
}

func (s *RPCErr) isStats() {}

// IsClient indicates if the stats is a client stats.
func (s *RPCErr) IsClient() bool { return s.Client }

var (
	on      = new(int32)
	handler func(context.Context, RPCStats)
)

// On indicates whether stats is started.
func On() bool {
	return atomic.LoadInt32(on) == 1
}

// Handle returns the call back function registered by user to process the stats.
func Handle(ctx context.Context, s RPCStats) {
	handler(ctx, s)
}

// RegisterHandler registers the user handler function.
// If another handler was registered before, this new handler will overwrite the old one.
// This handler function will be called to process the stats.
func RegisterHandler(f func(context.Context, RPCStats)) {
	handler = f
}

// Start starts the stats collection.
// Stats will only be started if handler is not nil.
func Start() {
	if handler == nil {
		grpclog.Println("handler is nil when starting stats. Stats is not started")
		return
	}
	atomic.StoreInt32(on, 1)
}

// Stop stops the collection of any further stats.
// Stop won't unregister handler.
func Stop() {
	atomic.StoreInt32(on, 0)
}
<commit_msg>remove isStats from interface<commit_after>/*
 *
 * Copyright 2016, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// Package stats is for collecting and reporting various network and RPC stats.
// This package is for monitoring purpose only.
// All APIs are experimental.
package stats // import "google.golang.org/grpc/stats"

import (
	"net"
	"sync/atomic"
	"time"

	"golang.org/x/net/context"
	"google.golang.org/grpc/grpclog"
)

// RPCStats contains stats information about RPCs.
// All stats types in this package implements this interface.
type RPCStats interface {
	// IsClient indicates if the stats is a client stats.
	IsClient() bool
}

// InPayload contains the information for a incoming payload.
type InPayload struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// Payload is the payload with original type.
	Payload interface{}
	// Data is the unencrypted message payload.
	Data []byte
	// Length is the length of uncompressed data.
	Length int
	// WireLength is the length of data on wire (compressed, signed, encrypted).
	WireLength int
	// RecvTime is the time when the payload is received.
	RecvTime time.Time
}

// IsClient indicates if the stats is a client stats.
func (s *InPayload) IsClient() bool { return s.Client }

// InHeader indicates a header is received.
// Method, addresses and Encryption are only valid if Client is false.
type InHeader struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int

	// Method is the full RPC method string, i.e., /package.service/method.
	Method string
	// RemoteAddr is the remote address of the corresponding connection.
	RemoteAddr net.Addr
	// LocalAddr is the local address of the corresponding connection.
	LocalAddr net.Addr
	// Encryption is encrypt method used in the RPC.
	Encryption string
}

// IsClient indicates if the stats is a client stats.
func (s *InHeader) IsClient() bool { return s.Client }

// InTrailer indicates a trailer is received.
type InTrailer struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int
}

// IsClient indicates if the stats is a client stats.
func (s *InTrailer) IsClient() bool { return s.Client }

// OutPayload contains the information for a outgoing payload.
type OutPayload struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// Payload is the payload with original type.
	Payload interface{}
	// Data is the unencrypted message payload.
	Data []byte
	// Length is the length of uncompressed data.
	Length int
	// WireLength is the length of data on wire (compressed, signed, encrypted).
	WireLength int
	// SentTime is the time when the payload is sent.
	SentTime time.Time
}

// IsClient indicates if the stats is a client stats.
func (s *OutPayload) IsClient() bool { return s.Client }

// OutHeader indicates a header is sent.
// Method, addresses and Encryption are only valid if Client is true.
type OutHeader struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int

	// Method is the full RPC method string, i.e., /package.service/method.
	Method string
	// RemoteAddr is the remote address of the corresponding connection.
	RemoteAddr net.Addr
	// LocalAddr is the local address of the corresponding connection.
	LocalAddr net.Addr
	// Encryption is encrypt method used in the RPC.
	Encryption string
	// Failfast indicates if this RPC is failfast.
	FailFast bool
}

// IsClient indicates if the stats is a client stats.
func (s *OutHeader) IsClient() bool { return s.Client }

// OutTrailer indicates a trailer is sent.
type OutTrailer struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// WireLength is the wire length of header.
	WireLength int
}

// IsClient indicates if the stats is a client stats.
func (s *OutTrailer) IsClient() bool { return s.Client }

// RPCErr indicates an error happens.
type RPCErr struct {
	// Client indicates if this stats is a client stats.
	Client bool
	// Error is the error just happened. Its type is gRPC error.
	Error error
}

// IsClient indicates if the stats is a client stats.
func (s *RPCErr) IsClient() bool { return s.Client }

var (
	on      = new(int32)
	handler func(context.Context, RPCStats)
)

// On indicates whether stats is started.
func On() bool {
	return atomic.LoadInt32(on) == 1
}

// Handle returns the call back function registered by user to process the stats.
func Handle(ctx context.Context, s RPCStats) {
	handler(ctx, s)
}

// RegisterHandler registers the user handler function.
// If another handler was registered before, this new handler will overwrite the old one.
// This handler function will be called to process the stats.
func RegisterHandler(f func(context.Context, RPCStats)) {
	handler = f
}

// Start starts the stats collection.
// Stats will only be started if handler is not nil.
func Start() {
	if handler == nil {
		grpclog.Println("handler is nil when starting stats. Stats is not started")
		return
	}
	atomic.StoreInt32(on, 1)
}

// Stop stops the collection of any further stats.
// Stop won't unregister handler.
func Stop() {
	atomic.StoreInt32(on, 0)
}
<|endoftext|>


<commit_before>// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package suss

import (
	"io/ioutil"
	"os"
	"syscall"

	"golang.org/x/sys/unix"
)

// redirectOutput redirects stdout and stderr into
// a file so that printing during a test does not
// spew to the terminal. This allows us to only
// print the output from the final shrinked test
func redirectOutput() (*os.File, func(), error) {
	// grab the FDs so that we can reestablish
	// stdout/stderr
	// note that since we only close FDs that already
	// have copies elsewhere, it's not possible for them
	// to return errors
	// tempfile should be Close-on-exec, so we don't
	// have to do anything special to it
	tmpfile, err := ioutil.TempFile("", "suss")
	if err != nil {
		return nil, nil, err
	}
	stdout, stderr, err := getCopies()
	if err != nil {
		return nil, nil, err
	}

	// these dups will be not be close-on-exec,
	// which is what we want. If a test executes,
	// we should grab its stdout/stderr into the tmpfile
	err = unix.Dup2(int(tmpfile.Fd()), int(os.Stdout.Fd()))
	if err != nil {
		unix.Close(stdout)
		unix.Close(stderr)
		return nil, nil, err
	}
	err = unix.Dup2(int(tmpfile.Fd()), int(os.Stderr.Fd()))
	if err != nil {
		unix.Close(stderr)
		// dilemma, if we manage to dup stdout, but not
		// stderr, then we should reestablish stdout
		// however that can fail. Make a best attempt effort at reestablishing it
		_ = unix.Dup2(stdout, int(os.Stdout.Fd()))
		unix.Close(stdout)
		return nil, nil, err
	}
	// stdout and stdin are now pointed to our file and
	// the fds used for reestablishing them are close-on-exec
	return tmpfile, dupAndClose(stdout, stderr), nil
}

func dupAndClose(stdout, stderr int) func() {
	f := func() {
		err := unix.Dup2(stdout, int(os.Stdout.Fd()))
		if err != nil {
			panic(err)
		}
		err = unix.Dup2(stderr, int(os.Stderr.Fd()))
		if err != nil {
			panic(err)
		}
		unix.Close(stdout)
		unix.Close(stderr)
	}
	return f
}

// Unix is hard. The spare duplicated file descriptors
// should be close-on-exec in case a test executes
// a binary.
func getCopies() (stdout int, stderr int, err error) {
	syscall.ForkLock.RLock()
	defer syscall.ForkLock.RUnlock()
	stdout, err = unix.Dup(int(os.Stdout.Fd()))
	if err != nil {
		return 0, 0, err
	}
	stderr, err = unix.Dup(int(os.Stdout.Fd()))
	if err != nil {
		unix.Close(stdout)
		return 0, 0, err
	}
	// these functions do not return an error.
	// not handling it is intentional
	unix.CloseOnExec(stdout)
	unix.CloseOnExec(stderr)
	return stdout, stderr, nil
}
<commit_msg>fix double dup in stdout redirection<commit_after>// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris

package suss

import (
	"io/ioutil"
	"os"
	"syscall"

	"golang.org/x/sys/unix"
)

// redirectOutput redirects stdout and stderr into
// a file so that printing during a test does not
// spew to the terminal. This allows us to only
// print the output from the final shrinked test
func redirectOutput() (*os.File, func(), error) {
	// grab the FDs so that we can reestablish
	// stdout/stderr
	// note that since we only close FDs that already
	// have copies elsewhere, it's not possible for them
	// to return errors
	// tempfile should be Close-on-exec, so we don't
	// have to do anything special to it
	tmpfile, err := ioutil.TempFile("", "suss")
	if err != nil {
		return nil, nil, err
	}
	stdout, stderr, err := getCopies()
	if err != nil {
		return nil, nil, err
	}

	// these dups will be not be close-on-exec,
	// which is what we want. If a test executes,
	// we should grab its stdout/stderr into the tmpfile
	err = unix.Dup2(int(tmpfile.Fd()), int(os.Stdout.Fd()))
	if err != nil {
		unix.Close(stdout)
		unix.Close(stderr)
		return nil, nil, err
	}
	err = unix.Dup2(int(tmpfile.Fd()), int(os.Stderr.Fd()))
	if err != nil {
		unix.Close(stderr)
		// dilemma, if we manage to dup stdout, but not
		// stderr, then we should reestablish stdout
		// however that can fail. Make a best attempt effort at reestablishing it
		_ = unix.Dup2(stdout, int(os.Stdout.Fd()))
		unix.Close(stdout)
		return nil, nil, err
	}
	// stdout and stdin are now pointed to our file and
	// the fds used for reestablishing them are close-on-exec
	return tmpfile, dupAndClose(stdout, stderr), nil
}

func dupAndClose(stdout, stderr int) func() {
	f := func() {
		err := unix.Dup2(stdout, int(os.Stdout.Fd()))
		if err != nil {
			panic(err)
		}
		err = unix.Dup2(stderr, int(os.Stderr.Fd()))
		if err != nil {
			panic(err)
		}
		unix.Close(stdout)
		unix.Close(stderr)
	}
	return f
}

// Unix is hard. The spare duplicated file descriptors
// should be close-on-exec in case a test executes
// a binary.
func getCopies() (stdout int, stderr int, err error) {
	syscall.ForkLock.RLock()
	defer syscall.ForkLock.RUnlock()
	stdout, err = unix.Dup(int(os.Stdout.Fd()))
	if err != nil {
		return 0, 0, err
	}
	stderr, err = unix.Dup(int(os.Stderr.Fd()))
	if err != nil {
		unix.Close(stdout)
		return 0, 0, err
	}
	// these functions do not return an error.
	// not handling it is intentional
	unix.CloseOnExec(stdout)
	unix.CloseOnExec(stderr)
	return stdout, stderr, nil
}
<|endoftext|>


<commit_before>package class

func getVslot(class *Class, name, descriptor string) int {
	for i, m := range class.vtable {
		if m.name == name && m.descriptor == descriptor {
			return i
		}
	}
	// todo
	return -1
}

func createVtable(class *Class) {
	class.vtable = copySuperVtable(class)

	// class._eachMethod(func(m *Method) {

	// })
	for _, m := range class.methods {
		if !m.IsStatic() {
			if i := search(class.vtable, m); i > -1 {
				class.vtable[i] = m // override
			} else {
				addVmethod(class, m)
			}
		}
	}
}

func copySuperVtable(class *Class) []*Method {
	if class.superClass != nil {
		superVtable := class.superClass.vtable
		newVtable := make([]*Method, len(superVtable))
		copy(newVtable, superVtable)
		return newVtable
	} else {
		return nil
	}
}

func addVmethod(class *Class, m *Method) {
	_len := len(class.vtable)
	if _len == cap(class.vtable) {
		newVtable := make([]*Method, _len, _len+8)
		copy(newVtable, class.vtable)
		class.vtable = newVtable
	}

	class.vtable = append(class.vtable, m)
}

// func countNewVirtualMethod(class *Class) int {
// 	superVtable := getSuperVtable(class)

// 	count := 0
// 	class._eachMethod(func(m *Method) {
// 		if isVirtualMethod(m) && search(superVtable, m) < 0 {
// 			count++
// 		}
// 	})

// 	return count
// }

func isVirtualMethod(method *Method) bool {
	return !method.IsStatic() &&
		!method.IsFinal() &&
		!method.IsPrivate() &&
		method.Name() != constructorName
}

func search(vtable []*Method, m *Method) int {
	for i, vm := range vtable {
		if vm.name == m.name && vm.descriptor == m.descriptor {
			return i
		}
	}
	return -1
}

// visit all class and interface methods
func (self *Class) _eachMethod(f func(*Method)) {
	for _, m := range self.methods {
		f(m)
	}
	for _, iface := range self.interfaces {
		iface._eachMethod(f)
	}
}
<commit_msg>fixing vtable..<commit_after>package class

func getVslot(class *Class, name, descriptor string) int {
	for i, m := range class.vtable {
		if m.name == name && m.descriptor == descriptor {
			return i
		}
	}
	// todo
	return -1
}

func createVtable(class *Class) {
	class.vtable = copySuperVtable(class)

	// class._eachMethod(func(m *Method) {

	// })
	for _, m := range class.methods {
		if isVirtualMethod(m) {
			if i := search(class.vtable, m); i > -1 {
				class.vtable[i] = m // override
			} else {
				addVmethod(class, m)
			}
		}
	}
}

func copySuperVtable(class *Class) []*Method {
	if class.superClass != nil {
		superVtable := class.superClass.vtable
		newVtable := make([]*Method, len(superVtable))
		copy(newVtable, superVtable)
		return newVtable
	} else {
		return nil
	}
}

func addVmethod(class *Class, m *Method) {
	_len := len(class.vtable)
	if _len == cap(class.vtable) {
		newVtable := make([]*Method, _len, _len+8)
		copy(newVtable, class.vtable)
		class.vtable = newVtable
	}

	class.vtable = append(class.vtable, m)
}

func isVirtualMethod(method *Method) bool {
	return !method.IsStatic() &&
		//!method.IsFinal() &&
		!method.IsPrivate() &&
		method.Name() != constructorName
}

func search(vtable []*Method, m *Method) int {
	for i, vm := range vtable {
		if vm.name == m.name && vm.descriptor == m.descriptor {
			return i
		}
	}
	return -1
}

// visit all class and interface methods
func (self *Class) _eachMethod(f func(*Method)) {
	for _, m := range self.methods {
		f(m)
	}
	for _, iface := range self.interfaces {
		iface._eachMethod(f)
	}
}
<|endoftext|>


<commit_before>package store

import (
	"encoding/json"
	"errors"
	"net/http"
	"sort"
	"time"

	"github.com/aslanides/gocrud/x"
)

var (
	ErrNoParent = errors.New("No parent found")
)

// Query stores the read instrutions, storing the instruction set
// for the entities Query relates to.
type Query struct {
	kind       string
	id         string
	filterOut  map[string]bool
	maxDepth   int
	children   []*Query
	parent     *Query
	getDeleted bool
}

type Object struct {
	Value  interface{}
	Source string
	NanoTs int64
}

type Versions struct {
	versions []Object
}

// Result stores the final entity state retrieved from Store upon running
// the instructions provided by Query.
type Result struct {
	Id       string
	Kind     string
	Columns  map[string]*Versions
	Children []*Result
}

type runResult struct {
	Result *Result
	Err    error
}

func (v *Versions) add(o Object) {
	if len(v.versions) > 0 {
		i := len(v.versions) - 1
		if v.versions[i].NanoTs > o.NanoTs {
			// unsorted list. Gocrud code is doing something wrong.
			log.Fatal("Appending an object with lower ts to a sorted list")
		}
	}
	v.versions = append(v.versions, o)
}

func (v Versions) Latest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	i := len(v.versions) - 1
	return v.versions[i]
}

func (v Versions) Oldest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	return v.versions[0]
}

func (v Versions) Count() int {
	return len(v.versions)
}

// Retrieve the parent id for given entity id. Return ErrNoParent if parent is
// not present. Otherwise, if an error occurs during retrieval, returns that.
func Parent(id string) (parentid string, rerr error) {
	its, err := Get().GetEntity(id)
	if err != nil {
		x.LogErr(log, err).WithField("id", id).Error("While retrieving entity")
		return "", err
	}
	for _, it := range its {
		if it.Predicate == "_parent_" {
			return it.ObjectId, nil
		}
	}
	return "", ErrNoParent
}

// NewQuery is the main entrypoint to data store queries. Returns back a Query
// object pointer, to run read instructions on.
func NewQuery(id string) *Query {
	q := new(Query)
	q.id = id
	return q
}

// UptoDepth specifies the number of levels of descendants that would be
// retrieved for the entity Query points to.
//
// You can think of this in terms of a tree structure, where the Entity pointed
// to by Query points to other Entities, which in turn point to more Entities,
// and so on. For e.g. Post -> Comments -> Likes.
func (q *Query) UptoDepth(level int) *Query {
	q.maxDepth = level
	return q
}

func (q *Query) AllowDeleted() *Query {
	q.getDeleted = true
	return q
}

// Collect specifies the kind of child entities to retrieve. Returns back
// a new Query pointer pointing to those children entities as a collective.
//
// Any further operations on this returned pointer would attribute to those
// children entities, and not the caller query entity.
func (q *Query) Collect(kind string) *Query {
	for _, child := range q.children {
		if child.kind == kind {
			return child
		}
	}
	child := new(Query)
	child.parent = q
	child.kind = kind
	child.getDeleted = q.getDeleted
	q.children = append(q.children, child)
	return child
}

// FilterOut provides a way to well, filter out, any entities which have
// the given property.
func (q *Query) FilterOut(property string) *Query {
	if len(q.filterOut) == 0 {
		q.filterOut = make(map[string]bool)
	}
	q.filterOut[property] = true
	return q
}

func (q *Query) root() *Query {
	for q.parent != nil {
		q = q.parent
	}
	return q
}

func (q *Query) doRun(level, max int, ch chan runResult) {
	log.Debugf("Query: %+v", q)
	its, err := Get().GetEntity(q.id)
	if err != nil {
		x.LogErr(log, err).Error("While retrieving: ", q.id)
		ch <- runResult{Result: nil, Err: err}
		return
	}
	if len(its) == 0 {
		ch <- runResult{Result: new(Result), Err: nil}
		return
	}
	sort.Sort(x.Its(its))

	follow := make(map[string]*Query)
	for _, child := range q.children {
		follow[child.kind] = child
		log.WithField("kind", child.kind).WithField("child", child).Debug("Following")
	}

	result := new(Result)
	result.Columns = make(map[string]*Versions)
	it := its[0]
	result.Id = it.SubjectId
	result.Kind = it.SubjectType

	waitTimes := 0
	childChan := make(chan runResult)
	for _, it := range its {
		if it.Predicate == "_delete_" && !q.getDeleted {
			var del bool
			err := json.Unmarshal(it.Object, &del)
			if err != nil {
				log.Errorf("While unmarshalling %v: %v", string(it.Object), err)
				return
			}
			if del {
				// If marked as deleted, don't return this node.
				log.WithField("id", result.Id).
					WithField("kind", result.Kind).
					WithField("_delete_", true).
					Debug("Discarding due to delete bit")
				ch <- runResult{Result: new(Result), Err: nil}
				return
			}
		}

		if it.Predicate == "_parent_" {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("parent", it.ObjectId).
				Debug("Not following edge back to parent")
			continue
		}

		if _, fout := q.filterOut[it.Predicate]; fout {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("predicate", it.Predicate).
				Debug("Discarding due to predicate filter")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if len(it.ObjectId) == 0 {
			o := Object{NanoTs: it.NanoTs, Source: it.Source}
			if err := json.Unmarshal(it.Object, &o.Value); err != nil {
				x.LogErr(log, err).Error("While unmarshal")
				ch <- runResult{Result: nil, Err: err}
				return
			}

			if _, vok := result.Columns[it.Predicate]; !vok {
				result.Columns[it.Predicate] = new(Versions)
			}
			result.Columns[it.Predicate].add(o)
			continue
		}

		if childq, fw := follow[it.Predicate]; fw {
			nchildq := new(Query)
			*nchildq = *childq // This is important, otherwise id gets overwritten
			nchildq.id = it.ObjectId
			nchildq.getDeleted = q.getDeleted

			// Use child's maxDepth here, instead of parent's.
			waitTimes += 1
			log.WithField("child_id", nchildq.id).
				WithField("child_kind", nchildq.kind).Debug("Go routine for child")
			go nchildq.doRun(0, nchildq.maxDepth, childChan)
			continue
		}

		if len(it.ObjectId) > 0 && level < max {
			child := new(Query)
			child.id = it.ObjectId
			child.getDeleted = q.getDeleted

			waitTimes += 1
			log.WithField("child_id", child.id).WithField("level", level+1).
				Debug("Go routine for child one level deeper")
			go child.doRun(level+1, max, childChan)
		}
	}

	// Wait for all those subroutines
	for i := 0; i < waitTimes; i++ {
		log.Debugf("Waiting for children subroutines: %v/%v", i, waitTimes-1)
		rr := <-childChan
		log.Debugf("Waiting done")
		if rr.Err != nil {
			x.LogErr(log, err).Error("While child doRun")
		} else {
			if len(rr.Result.Id) > 0 && len(rr.Result.Kind) > 0 {
				log.WithField("result", *rr.Result).Debug("Appending child")
				result.Children = append(result.Children, rr.Result)
			}
		}
	}

	ch <- runResult{Result: result, Err: nil}
	return
}

// Run finds the root from the given Query pointer, recursively executes
// the read operations, and returns back pointer to Result object.
// Any errors encountered during these stpeps is returned as well.
func (q *Query) Run() (result *Result, rerr error) {
	q = q.root()
	if len(q.id) == 0 {
		return result, errors.New("Empty entity id")
	}

	ch := make(chan runResult)
	go q.doRun(0, q.maxDepth, ch)
	rr := <-ch // Blocking wait
	return rr.Result, rr.Err
}

func (r *Result) Drop(pred string) {
	delete(r.Columns, pred)
}

func (r *Result) Debug(level int) {
	log.Debugf("Result level %v: %+v", level, r)
	for _, child := range r.Children {
		child.Debug(level + 1)
	}
}

func (r *Result) ToMap() (data map[string]interface{}) {
	data = make(map[string]interface{})
	data["id"] = r.Id
	data["kind"] = r.Kind
	var ts_latest int64
	ts_oldest := time.Now().UnixNano()
	for pred, versions := range r.Columns {
		// During conversion to JSON, to keep things simple,
		// we're dropping older versions of predicates, and
		// source and ts information across all the predicates,
		// keeping only the latest one.
		data[pred] = versions.Latest().Value
		if versions.Latest().NanoTs > ts_latest {
			ts_latest = versions.Latest().NanoTs
			data["modifier"] = versions.Latest().Source // Loss of information.
		}
		if versions.Oldest().NanoTs < ts_oldest {
			ts_oldest = versions.Oldest().NanoTs
			data["creator"] = versions.Oldest().Source
		}
	}
	data["creation_ms"] = int(ts_oldest / 1000000)
	data["modification_ms"] = int(ts_latest / 1000000) // Loss of information. Picking up latest mod time.
	kinds := make(map[string]bool)
	for _, child := range r.Children {
		kinds[child.Kind] = true
	}

	for kind := range kinds {
		var l []map[string]interface{}
		for _, child := range r.Children {
			if kind != child.Kind {
				continue
			}
			l = append(l, child.ToMap())
		}
		data[kind] = l
	}

	return
}

// ToJson creates the JSON for the data pointed by the Result pointer.
//
// Note that this doesn't find the "root" from the Result pointer, instead
// doing the processing only from the current Result pointer.
func (r *Result) ToJson() ([]byte, error) {
	data := r.ToMap()
	return json.Marshal(data)
}

// WriteJsonResponse does the same as ToJson. But also writes the JSON
// generated to http.ResponseWriter. In case of error, writes that error
// instead, in this format:
//
//  {
//   "code":    "E_ERROR",
//   "message": "error message"
//  }
func (r *Result) WriteJsonResponse(w http.ResponseWriter) {
	data, err := r.ToJson()
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
	_, err = w.Write(data)
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
}
<commit_msg>Revert "Look at _delete_ values"<commit_after>package store

import (
	"encoding/json"
	"errors"
	"net/http"
	"sort"
	"time"

	"github.com/aslanides/gocrud/x"
)

var (
	ErrNoParent = errors.New("No parent found")
)

// Query stores the read instrutions, storing the instruction set
// for the entities Query relates to.
type Query struct {
	kind       string
	id         string
	filterOut  map[string]bool
	maxDepth   int
	children   []*Query
	parent     *Query
	getDeleted bool
}

type Object struct {
	Value  interface{}
	Source string
	NanoTs int64
}

type Versions struct {
	versions []Object
}

// Result stores the final entity state retrieved from Store upon running
// the instructions provided by Query.
type Result struct {
	Id       string
	Kind     string
	Columns  map[string]*Versions
	Children []*Result
}

type runResult struct {
	Result *Result
	Err    error
}

func (v *Versions) add(o Object) {
	if len(v.versions) > 0 {
		i := len(v.versions) - 1
		if v.versions[i].NanoTs > o.NanoTs {
			// unsorted list. Gocrud code is doing something wrong.
			log.Fatal("Appending an object with lower ts to a sorted list")
		}
	}
	v.versions = append(v.versions, o)
}

func (v Versions) Latest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	i := len(v.versions) - 1
	return v.versions[i]
}

func (v Versions) Oldest() Object {
	if len(v.versions) == 0 {
		return Object{}
	}
	return v.versions[0]
}

func (v Versions) Count() int {
	return len(v.versions)
}

// Retrieve the parent id for given entity id. Return ErrNoParent if parent is
// not present. Otherwise, if an error occurs during retrieval, returns that.
func Parent(id string) (parentid string, rerr error) {
	its, err := Get().GetEntity(id)
	if err != nil {
		x.LogErr(log, err).WithField("id", id).Error("While retrieving entity")
		return "", err
	}
	for _, it := range its {
		if it.Predicate == "_parent_" {
			return it.ObjectId, nil
		}
	}
	return "", ErrNoParent
}

// NewQuery is the main entrypoint to data store queries. Returns back a Query
// object pointer, to run read instructions on.
func NewQuery(id string) *Query {
	q := new(Query)
	q.id = id
	return q
}

// UptoDepth specifies the number of levels of descendants that would be
// retrieved for the entity Query points to.
//
// You can think of this in terms of a tree structure, where the Entity pointed
// to by Query points to other Entities, which in turn point to more Entities,
// and so on. For e.g. Post -> Comments -> Likes.
func (q *Query) UptoDepth(level int) *Query {
	q.maxDepth = level
	return q
}

func (q *Query) AllowDeleted() *Query {
	q.getDeleted = true
	return q
}

// Collect specifies the kind of child entities to retrieve. Returns back
// a new Query pointer pointing to those children entities as a collective.
//
// Any further operations on this returned pointer would attribute to those
// children entities, and not the caller query entity.
func (q *Query) Collect(kind string) *Query {
	for _, child := range q.children {
		if child.kind == kind {
			return child
		}
	}
	child := new(Query)
	child.parent = q
	child.kind = kind
	child.getDeleted = q.getDeleted
	q.children = append(q.children, child)
	return child
}

// FilterOut provides a way to well, filter out, any entities which have
// the given property.
func (q *Query) FilterOut(property string) *Query {
	if len(q.filterOut) == 0 {
		q.filterOut = make(map[string]bool)
	}
	q.filterOut[property] = true
	return q
}

func (q *Query) root() *Query {
	for q.parent != nil {
		q = q.parent
	}
	return q
}

func (q *Query) doRun(level, max int, ch chan runResult) {
	log.Debugf("Query: %+v", q)
	its, err := Get().GetEntity(q.id)
	if err != nil {
		x.LogErr(log, err).Error("While retrieving: ", q.id)
		ch <- runResult{Result: nil, Err: err}
		return
	}
	if len(its) == 0 {
		ch <- runResult{Result: new(Result), Err: nil}
		return
	}
	sort.Sort(x.Its(its))

	follow := make(map[string]*Query)
	for _, child := range q.children {
		follow[child.kind] = child
		log.WithField("kind", child.kind).WithField("child", child).Debug("Following")
	}

	result := new(Result)
	result.Columns = make(map[string]*Versions)
	it := its[0]
	result.Id = it.SubjectId
	result.Kind = it.SubjectType

	waitTimes := 0
	childChan := make(chan runResult)
	for _, it := range its {
		if it.Predicate == "_delete_" && !q.getDeleted {
			// If marked as deleted, don't return this node.
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("_delete_", true).
				Debug("Discarding due to delete bit")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if it.Predicate == "_parent_" {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("parent", it.ObjectId).
				Debug("Not following edge back to parent")
			continue
		}

		if _, fout := q.filterOut[it.Predicate]; fout {
			log.WithField("id", result.Id).
				WithField("kind", result.Kind).
				WithField("predicate", it.Predicate).
				Debug("Discarding due to predicate filter")
			ch <- runResult{Result: new(Result), Err: nil}
			return
		}

		if len(it.ObjectId) == 0 {
			o := Object{NanoTs: it.NanoTs, Source: it.Source}
			if err := json.Unmarshal(it.Object, &o.Value); err != nil {
				x.LogErr(log, err).Error("While unmarshal")
				ch <- runResult{Result: nil, Err: err}
				return
			}

			if _, vok := result.Columns[it.Predicate]; !vok {
				result.Columns[it.Predicate] = new(Versions)
			}
			result.Columns[it.Predicate].add(o)
			continue
		}

		if childq, fw := follow[it.Predicate]; fw {
			nchildq := new(Query)
			*nchildq = *childq // This is important, otherwise id gets overwritten
			nchildq.id = it.ObjectId
			nchildq.getDeleted = q.getDeleted

			// Use child's maxDepth here, instead of parent's.
			waitTimes += 1
			log.WithField("child_id", nchildq.id).
				WithField("child_kind", nchildq.kind).Debug("Go routine for child")
			go nchildq.doRun(0, nchildq.maxDepth, childChan)
			continue
		}

		if len(it.ObjectId) > 0 && level < max {
			child := new(Query)
			child.id = it.ObjectId
			child.getDeleted = q.getDeleted

			waitTimes += 1
			log.WithField("child_id", child.id).WithField("level", level+1).
				Debug("Go routine for child one level deeper")
			go child.doRun(level+1, max, childChan)
		}
	}

	// Wait for all those subroutines
	for i := 0; i < waitTimes; i++ {
		log.Debugf("Waiting for children subroutines: %v/%v", i, waitTimes-1)
		rr := <-childChan
		log.Debugf("Waiting done")
		if rr.Err != nil {
			x.LogErr(log, err).Error("While child doRun")
		} else {
			if len(rr.Result.Id) > 0 && len(rr.Result.Kind) > 0 {
				log.WithField("result", *rr.Result).Debug("Appending child")
				result.Children = append(result.Children, rr.Result)
			}
		}
	}

	ch <- runResult{Result: result, Err: nil}
	return
}

// Run finds the root from the given Query pointer, recursively executes
// the read operations, and returns back pointer to Result object.
// Any errors encountered during these stpeps is returned as well.
func (q *Query) Run() (result *Result, rerr error) {
	q = q.root()
	if len(q.id) == 0 {
		return result, errors.New("Empty entity id")
	}

	ch := make(chan runResult)
	go q.doRun(0, q.maxDepth, ch)
	rr := <-ch // Blocking wait
	return rr.Result, rr.Err
}

func (r *Result) Drop(pred string) {
	delete(r.Columns, pred)
}

func (r *Result) Debug(level int) {
	log.Debugf("Result level %v: %+v", level, r)
	for _, child := range r.Children {
		child.Debug(level + 1)
	}
}

func (r *Result) ToMap() (data map[string]interface{}) {
	data = make(map[string]interface{})
	data["id"] = r.Id
	data["kind"] = r.Kind
	var ts_latest int64
	ts_oldest := time.Now().UnixNano()
	for pred, versions := range r.Columns {
		// During conversion to JSON, to keep things simple,
		// we're dropping older versions of predicates, and
		// source and ts information across all the predicates,
		// keeping only the latest one.
		data[pred] = versions.Latest().Value
		if versions.Latest().NanoTs > ts_latest {
			ts_latest = versions.Latest().NanoTs
			data["modifier"] = versions.Latest().Source // Loss of information.
		}
		if versions.Oldest().NanoTs < ts_oldest {
			ts_oldest = versions.Oldest().NanoTs
			data["creator"] = versions.Oldest().Source
		}
	}
	data["creation_ms"] = int(ts_oldest / 1000000)
	data["modification_ms"] = int(ts_latest / 1000000) // Loss of information. Picking up latest mod time.
	kinds := make(map[string]bool)
	for _, child := range r.Children {
		kinds[child.Kind] = true
	}

	for kind := range kinds {
		var l []map[string]interface{}
		for _, child := range r.Children {
			if kind != child.Kind {
				continue
			}
			l = append(l, child.ToMap())
		}
		data[kind] = l
	}

	return
}

// ToJson creates the JSON for the data pointed by the Result pointer.
//
// Note that this doesn't find the "root" from the Result pointer, instead
// doing the processing only from the current Result pointer.
func (r *Result) ToJson() ([]byte, error) {
	data := r.ToMap()
	return json.Marshal(data)
}

// WriteJsonResponse does the same as ToJson. But also writes the JSON
// generated to http.ResponseWriter. In case of error, writes that error
// instead, in this format:
//
//  {
//   "code":    "E_ERROR",
//   "message": "error message"
//  }
func (r *Result) WriteJsonResponse(w http.ResponseWriter) {
	data, err := r.ToJson()
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
	_, err = w.Write(data)
	if err != nil {
		x.SetStatus(w, x.E_ERROR, err.Error())
		return
	}
}
<|endoftext|>


<commit_before>package store

import (
	"database/sql"
	"fmt"
	"github.com/evoL/gif/config"
	"github.com/evoL/gif/db"
	. "github.com/evoL/gif/image"
	"io/ioutil"
	"os"
	"path"
	"time"
)

type Store struct {
	path string
	db   *sql.DB
}

type ExportedImage struct {
	Id   string
	Url  string
	Tags []string
}

type ExportFormat struct {
	Creator string
	Images  []ExportedImage
}

type TagInformation struct {
	Tag   string
	Count int64
}

func Default() (*Store, error) {
	return New(config.Global.StorePath)
}

func New(path string) (*Store, error) {
	defaultDb, err := db.Default()
	if err != nil {
		return nil, err
	}

	store := &Store{
		path: path,
		db:   defaultDb,
	}

	if err := os.MkdirAll(path, 0755); err != nil {
		return nil, err
	}
	return store, nil
}

func (store *Store) Add(image *Image) error {
	if err := ioutil.WriteFile(store.PathFor(image), image.Data, 0644); err != nil {
		return err
	}

	tx, err := store.db.Begin()
	if err != nil {
		return err
	}
	stmt, err := tx.Prepare("INSERT INTO images (id, url, added_at) VALUES (?, ?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()

	now := time.Now()

	if image.Url == "" {
		_, err = stmt.Exec(image.Id, sql.NullString{}, now)
	} else {
		_, err = stmt.Exec(image.Id, image.Url, now)
	}
	if err != nil {
		return err
	}

	image.AddedAt = &now

	return tx.Commit()
}

func (store *Store) Close() error {
	return store.db.Close()
}

func (store *Store) PathFor(image *Image) string {
	return path.Join(store.path, image.Id+".gif")
}

func (store *Store) Contains(image *Image) bool {
	var result bool
	store.db.QueryRow("SELECT (COUNT(*) > 0) AS result FROM images WHERE id = ? OR url = ?", image.Id, image.Url).Scan(&result)
	return result
}

func (store *Store) Find(filter Filter) (image *Image, err error) {
	limitedFilter := Limiter{Filter: filter, Limit: 1}

	var imageSlice []Image
	imageSlice, err = store.List(limitedFilter)
	if err != nil {
		return
	}

	if len(imageSlice) == 0 {
		return
	}

	image = &imageSlice[0]
	return
}

func (store *Store) Get(imageId string) (*Image, error) {
	return store.Find(ExactIdFilter{Id: imageId})
}

func (store *Store) List(filter Filter) (result []Image, err error) {
	result = make([]Image, 0)

	// The code assumes that the database returns images that aren't mixed with each
	// other; that is, the following situation doesn't happen:
	//
	// id | tag
	// 0  | a
	// 1  | b
	// 0  | c
	//
	// This is extremely unlikely, because those images would be added at the exact
	// same time. However, to prevent this, "ORDER BY id" needs to be added.
	// I didn't include this now, because it might add an unnecessary
	// performance hit.

	queryString := fmt.Sprintf(`
	SELECT id, tag, url, added_at
	FROM (
		SELECT DISTINCT images.*
		FROM images
		LEFT JOIN image_tags
		ON images.id = image_tags.image_id
		WHERE %s
	) images
	LEFT JOIN image_tags
	ON images.id = image_tags.image_id`, filter.Condition())

	// fmt.Println(queryString)

	rows, err := store.db.Query(queryString, filter.Values()...)
	if err != nil {
		return
	}
	defer rows.Close()

	img := Image{}
	for rows.Next() {
		var (
			id      string
			url     sql.NullString
			addedAt time.Time
			tag     sql.NullString
		)

		err = rows.Scan(&id, &tag, &url, &addedAt)
		if err != nil {
			return
		}

		if img.Id == id {
			if tag.Valid {
				// Add another tag to existing image
				img.Tags = append(img.Tags, tag.String)
			}
			// The opposite case should not happen, but if it does, we're just ignoring it.
		} else {
			// Append the previously processed image if there's one
			if img.Id != "" {
				result = append(result, img)
			}

			// Create a new image
			img.Id = id
			img.AddedAt = &addedAt
			if url.Valid {
				img.Url = url.String
			} else {
				img.Url = ""
			}
			if tag.Valid {
				img.Tags = []string{tag.String}
			} else {
				img.Tags = []string{}
			}

			// Fetch file size
			var info os.FileInfo
			info, err = os.Stat(store.PathFor(&img))
			if err != nil {
				return
			}
			img.Size = uint64(info.Size())
		}
	}

	// As we're only appending when a different image turns up, we need to append
	// the last image separately.
	if img.Id != "" {
		result = append(result, img)
	}

	err = rows.Err()
	return
}

func (store *Store) ListTags(filter Filter) (result []TagInformation, err error) {
	result = make([]TagInformation, 0)

	queryString := fmt.Sprintf(`
		SELECT tag, COUNT(*)
		FROM image_tags
		WHERE %s
		GROUP BY tag
		ORDER BY tag ASC`, filter.Condition())

	rows, err := store.db.Query(queryString, filter.Values()...)
	if err != nil {
		return
	}
	defer rows.Close()

	tagInfo := TagInformation{}
	for rows.Next() {
		err = rows.Scan(&tagInfo.Tag, &tagInfo.Count)
		if err != nil {
			return
		}

		result = append(result, tagInfo)
	}

	err = rows.Err()
	return
}

func (store *Store) UpdateUrl(image *Image, url string) error {
	tx, err := store.db.Begin()
	if err != nil {
		return err
	}

	_, err = tx.Exec("UPDATE images SET url = ? WHERE id = ?", url, image.Id)
	if err != nil {
		return err
	}

	err = tx.Commit()
	if err != nil {
		return err
	}

	image.Url = url

	return nil
}

func (store *Store) UpdateTags(image *Image, tags []string) error {
	tx, err := store.db.Begin()
	if err != nil {
		return err
	}

	// First, remove old tags
	_, err = tx.Exec("DELETE FROM image_tags WHERE image_id = ?", image.Id)
	if err != nil {
		return err
	}

	// Second, add new ones
	stmt, err := tx.Prepare("INSERT INTO image_tags (image_id, tag) VALUES (?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, tag := range tags {
		_, err = stmt.Exec(image.Id, tag)
		if err != nil {
			return err
		}
	}

	err = tx.Commit()
	if err != nil {
		return err
	}

	image.Tags = tags

	return nil
}

func (s *Store) Remove(image *Image) error {
	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	// Remove the file
	err = os.Remove(s.PathFor(image))
	if err != nil {
		return err
	}

	// Remove from database
	_, err = tx.Exec("DELETE FROM images WHERE id = ?", image.Id)
	if err != nil {
		return err
	}

	err = tx.Commit()
	if err != nil {
		return err
	}

	image.AddedAt = nil

	return nil
}

func (s *Store) RemoveAll(images []Image) (err error) {
	for _, img := range images {
		if err = s.Remove(&img); err != nil {
			return
		}
	}
	return
}

func (s *Store) Hydrate(img *Image) (err error) {
	path := s.PathFor(img)
	img.Data, err = ioutil.ReadFile(path)
	return
}
<commit_msg>Add a notion of schema versions<commit_after>package store

import (
	"database/sql"
	"fmt"
	"github.com/evoL/gif/config"
	"github.com/evoL/gif/db"
	. "github.com/evoL/gif/image"
	"io/ioutil"
	"os"
	"path"
	"time"
)

type Store struct {
	path string
	db   *sql.DB
}

type ExportedImage struct {
	Id   string
	Url  string
	Tags []string
}

type ExportFormat struct {
	Creator string
	Images  []ExportedImage
}

type TagInformation struct {
	Tag   string
	Count int64
}

func Default() (*Store, error) {
	return New(config.Global.StorePath)
}

func New(path string) (*Store, error) {
	defaultDb, err := db.Default()
	if err != nil {
		return nil, err
	}

	store := &Store{
		path: path,
		db:   defaultDb,
	}

	if err := os.MkdirAll(path, 0755); err != nil {
		return nil, err
	}
	return store, nil
}

func (store *Store) Add(image *Image) error {
	if err := ioutil.WriteFile(store.PathFor(image), image.Data, 0644); err != nil {
		return err
	}

	tx, err := store.db.Begin()
	if err != nil {
		return err
	}
	stmt, err := tx.Prepare("INSERT INTO images (id, url, added_at) VALUES (?, ?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()

	now := time.Now()

	if image.Url == "" {
		_, err = stmt.Exec(image.Id, sql.NullString{}, now)
	} else {
		_, err = stmt.Exec(image.Id, image.Url, now)
	}
	if err != nil {
		return err
	}

	image.AddedAt = &now

	return tx.Commit()
}

func (store *Store) Close() error {
	return store.db.Close()
}

func (store *Store) PathFor(image *Image) string {
	return path.Join(store.path, image.Id+".gif")
}

func (store *Store) Contains(image *Image) bool {
	var result bool
	store.db.QueryRow("SELECT (COUNT(*) > 0) AS result FROM images WHERE id = ? OR url = ?", image.Id, image.Url).Scan(&result)
	return result
}

func (store *Store) Find(filter Filter) (image *Image, err error) {
	limitedFilter := Limiter{Filter: filter, Limit: 1}

	var imageSlice []Image
	imageSlice, err = store.List(limitedFilter)
	if err != nil {
		return
	}

	if len(imageSlice) == 0 {
		return
	}

	image = &imageSlice[0]
	return
}

func (store *Store) Get(imageId string) (*Image, error) {
	return store.Find(ExactIdFilter{Id: imageId})
}

func (store *Store) List(filter Filter) (result []Image, err error) {
	result = make([]Image, 0)

	// The code assumes that the database returns images that aren't mixed with each
	// other; that is, the following situation doesn't happen:
	//
	// id | tag
	// 0  | a
	// 1  | b
	// 0  | c
	//
	// This is extremely unlikely, because those images would be added at the exact
	// same time. However, to prevent this, "ORDER BY id" needs to be added.
	// I didn't include this now, because it might add an unnecessary
	// performance hit.

	queryString := fmt.Sprintf(`
	SELECT id, tag, url, added_at
	FROM (
		SELECT DISTINCT images.*
		FROM images
		LEFT JOIN image_tags
		ON images.id = image_tags.image_id
		WHERE %s
	) images
	LEFT JOIN image_tags
	ON images.id = image_tags.image_id`, filter.Condition())

	// fmt.Println(queryString)

	rows, err := store.db.Query(queryString, filter.Values()...)
	if err != nil {
		return
	}
	defer rows.Close()

	img := Image{}
	for rows.Next() {
		var (
			id      string
			url     sql.NullString
			addedAt time.Time
			tag     sql.NullString
		)

		err = rows.Scan(&id, &tag, &url, &addedAt)
		if err != nil {
			return
		}

		if img.Id == id {
			if tag.Valid {
				// Add another tag to existing image
				img.Tags = append(img.Tags, tag.String)
			}
			// The opposite case should not happen, but if it does, we're just ignoring it.
		} else {
			// Append the previously processed image if there's one
			if img.Id != "" {
				result = append(result, img)
			}

			// Create a new image
			img.Id = id
			img.AddedAt = &addedAt
			if url.Valid {
				img.Url = url.String
			} else {
				img.Url = ""
			}
			if tag.Valid {
				img.Tags = []string{tag.String}
			} else {
				img.Tags = []string{}
			}

			// Fetch file size
			var info os.FileInfo
			info, err = os.Stat(store.PathFor(&img))
			if err != nil {
				return
			}
			img.Size = uint64(info.Size())
		}
	}

	// As we're only appending when a different image turns up, we need to append
	// the last image separately.
	if img.Id != "" {
		result = append(result, img)
	}

	err = rows.Err()
	return
}

func (store *Store) ListTags(filter Filter) (result []TagInformation, err error) {
	result = make([]TagInformation, 0)

	queryString := fmt.Sprintf(`
		SELECT tag, COUNT(*)
		FROM image_tags
		WHERE %s
		GROUP BY tag
		ORDER BY tag ASC`, filter.Condition())

	rows, err := store.db.Query(queryString, filter.Values()...)
	if err != nil {
		return
	}
	defer rows.Close()

	tagInfo := TagInformation{}
	for rows.Next() {
		err = rows.Scan(&tagInfo.Tag, &tagInfo.Count)
		if err != nil {
			return
		}

		result = append(result, tagInfo)
	}

	err = rows.Err()
	return
}

func (store *Store) UpdateUrl(image *Image, url string) error {
	tx, err := store.db.Begin()
	if err != nil {
		return err
	}

	_, err = tx.Exec("UPDATE images SET url = ? WHERE id = ?", url, image.Id)
	if err != nil {
		return err
	}

	err = tx.Commit()
	if err != nil {
		return err
	}

	image.Url = url

	return nil
}

func (store *Store) UpdateTags(image *Image, tags []string) error {
	tx, err := store.db.Begin()
	if err != nil {
		return err
	}

	// First, remove old tags
	_, err = tx.Exec("DELETE FROM image_tags WHERE image_id = ?", image.Id)
	if err != nil {
		return err
	}

	// Second, add new ones
	stmt, err := tx.Prepare("INSERT INTO image_tags (image_id, tag) VALUES (?, ?)")
	if err != nil {
		return err
	}
	defer stmt.Close()

	for _, tag := range tags {
		_, err = stmt.Exec(image.Id, tag)
		if err != nil {
			return err
		}
	}

	err = tx.Commit()
	if err != nil {
		return err
	}

	image.Tags = tags

	return nil
}

func (s *Store) Remove(image *Image) error {
	tx, err := s.db.Begin()
	if err != nil {
		return err
	}

	// Remove the file
	err = os.Remove(s.PathFor(image))
	if err != nil {
		return err
	}

	// Remove from database
	_, err = tx.Exec("DELETE FROM images WHERE id = ?", image.Id)
	if err != nil {
		return err
	}

	err = tx.Commit()
	if err != nil {
		return err
	}

	image.AddedAt = nil

	return nil
}

func (s *Store) RemoveAll(images []Image) (err error) {
	for _, img := range images {
		if err = s.Remove(&img); err != nil {
			return
		}
	}
	return
}

func (s *Store) Hydrate(img *Image) (err error) {
	path := s.PathFor(img)
	img.Data, err = ioutil.ReadFile(path)
	return
}

func (s *Store) Version() (int, error) {
	var version int64 = 0

	err := s.db.QueryRow("PRAGMA user_version").Scan(&version)

	// If not specified, the version is 1
	if err == nil && version < 1 {
		version = 1
	}

	return int(version), err
}
<|endoftext|>


<commit_before>// Package store abstracts the persistent storage used by elvish.
package store

import (
	"database/sql"
	"fmt"
	"net/url"
	"sync"

	_ "github.com/elves/go-sqlite/sqlite3" // enable the "sqlite3" SQL driver
)

// Store is the permanent storage backend for elvish.
type Store struct {
	db    *sql.DB
	Waits sync.WaitGroup
}

var initTable = map[string](func(*sql.DB) error){}

// DefaultDB returns the default database for storage.
func DefaultDB(dbname string) (*sql.DB, error) {
	uri := "file:" + url.QueryEscape(dbname) +
		"?mode=rwc&cache=shared&vfs=unix-dotfile"
	return sql.Open("sqlite3", uri)
}

// NewStore creates a new Store with the default database.
func NewStore(dbname string) (*Store, error) {
	db, err := DefaultDB(dbname)
	if err != nil {
		return nil, err
	}
	return NewStoreDB(db)
}

// NewStoreDB creates a new Store with a custom database. The database must be
// a SQLite database.
func NewStoreDB(db *sql.DB) (*Store, error) {
	st := &Store{db, sync.WaitGroup{}}

	for name, fn := range initTable {
		err := fn(db)
		if err != nil {
			return nil, fmt.Errorf("failed to initialize table %s: %v", name, err)
		}
	}

	return st, nil
}

func (s *Store) Close() error {
	if s == nil || s.db == nil {
		return nil
	}
	s.Waits.Wait()
	return s.db.Close()
}
<commit_msg>Go back to mattn/go-sqlite3.<commit_after>// Package store abstracts the persistent storage used by elvish.
package store

import (
	"database/sql"
	"fmt"
	"net/url"
	"sync"

	_ "github.com/mattn/go-sqlite3" // enable the "sqlite3" SQL driver
)

// Store is the permanent storage backend for elvish.
type Store struct {
	db    *sql.DB
	Waits sync.WaitGroup
}

var initTable = map[string](func(*sql.DB) error){}

// DefaultDB returns the default database for storage.
func DefaultDB(dbname string) (*sql.DB, error) {
	uri := "file:" + url.QueryEscape(dbname) +
		"?mode=rwc&cache=shared&vfs=unix-dotfile"
	return sql.Open("sqlite3", uri)
}

// NewStore creates a new Store with the default database.
func NewStore(dbname string) (*Store, error) {
	db, err := DefaultDB(dbname)
	if err != nil {
		return nil, err
	}
	return NewStoreDB(db)
}

// NewStoreDB creates a new Store with a custom database. The database must be
// a SQLite database.
func NewStoreDB(db *sql.DB) (*Store, error) {
	st := &Store{db, sync.WaitGroup{}}

	for name, fn := range initTable {
		err := fn(db)
		if err != nil {
			return nil, fmt.Errorf("failed to initialize table %s: %v", name, err)
		}
	}

	return st, nil
}

func (s *Store) Close() error {
	if s == nil || s.db == nil {
		return nil
	}
	s.Waits.Wait()
	return s.db.Close()
}
<|endoftext|>


<commit_before>package slices

import (
	"fmt"
	"reflect"
	"testing"
)

var ()

func expect(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to _expect. Args passed: %v", args)
	case 2:
		return _expectBasic(args[0], args[1])
	default:
		return _expectLabelled(args[0], args[1], args[2])
	}
}

func _expectBasic(result interface{}, expected interface{}) error {
	if !_areEqual(result, expected) {
		return fmt.Errorf("Expected '%v' to equal '%v'", result, expected)
	}
	return nil
}

func _expectLabelled(result interface{}, expected interface{}, label interface{}) error {
	if !_areEqual(result, expected) {
		return fmt.Errorf("%v: Expected '%v' to equal '%v'", label, result, expected)
	}
	return nil
}

func _areEqual(result interface{}, expected interface{}) bool {
	return reflect.DeepEqual(result, expected)
}

func expected(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to _errExpected. Args passed: %v", args)
	case 2:
		return fmt.Errorf("Expected '%v' to equal '%v'", args[0], args[1])
	default:
		return fmt.Errorf("%v: Expected '%v' to equal '%v'", args[0], args[1], args[2])
	}
}

func TestIsEmpty(t *testing.T) {

	if err := expect(IsEmpty([]string{}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{""}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{"a", "b", "c"}), false); err != nil {
		t.Error(err)
	}
}

func TestConcat(t *testing.T) {

	if err := expect(Concat([]string{}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{""}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{"a", "b", "c"}), "abc"); err != nil {
		t.Error(err)
	}
}

func TestJoin(t *testing.T) {

	if err := expect(Join([]string{}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{""}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{"a", "b", "c"}, "-"), "a-b-c"); err != nil {
		t.Error(err)
	}
}

func toPointer(s []string) *[]string {
	return &s
}

func TestCut(t *testing.T) {

	s := []string{}
	u := []string{}
	t.Logf("1%#v:", toPointer(s))
	t.Logf("2%#v:", toPointer(u))

	if err := expect(Cut([]string{}, 0, -1), []string{}); err != nil {
		t.Error(err)
	}

	if err := expect(Cut([]string{}, 0, -1), []string{""}); err != nil {
		t.Error(err)
	}

	// if err := expect(Cut([]string{""}, "-"), ""); err != nil {
	// 	t.Error(err)
	// }

	// if err := expect(Cut([]string{"a", "b", "c"}, "-"), "a-b-c"); err != nil {
	// 	t.Error(err)
	// }
}

// func BenchmarkStringsReplaceAll(b *testing.B) {
// 	for i := 0; i < b.N; i++ {
// 		strings.Replace(TestFileContent, ToFind, ToReplace, -1)
// 	}
// }
<commit_msg>Noted Terminal command to run test<commit_after>package slices

import (
	"fmt"
	"reflect"
	"testing"
)

// https://golang.org/cmd/go/#hdr-Description_of_testing_flags
// go test -v -bench string_test.go -benchtime 2s

var ()

func expect(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to _expect. Args passed: %v", args)
	case 2:
		return _expectBasic(args[0], args[1])
	default:
		return _expectLabelled(args[0], args[1], args[2])
	}
}

func _expectBasic(result interface{}, expected interface{}) error {
	if !_areEqual(result, expected) {
		return fmt.Errorf("Expected '%v' to equal '%v'", result, expected)
	}
	return nil
}

func _expectLabelled(result interface{}, expected interface{}, label interface{}) error {
	if !_areEqual(result, expected) {
		return fmt.Errorf("%v: Expected '%v' to equal '%v'", label, result, expected)
	}
	return nil
}

func _areEqual(result interface{}, expected interface{}) bool {
	return reflect.DeepEqual(result, expected)
}

func expected(args ...interface{}) error {
	switch len(args) {
	case 0, 1:
		return fmt.Errorf("Not enough arguments to _errExpected. Args passed: %v", args)
	case 2:
		return fmt.Errorf("Expected '%v' to equal '%v'", args[0], args[1])
	default:
		return fmt.Errorf("%v: Expected '%v' to equal '%v'", args[0], args[1], args[2])
	}
}

func TestIsEmpty(t *testing.T) {

	if err := expect(IsEmpty([]string{}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{""}), true); err != nil {
		t.Error(err)
	}

	if err := expect(IsEmpty([]string{"a", "b", "c"}), false); err != nil {
		t.Error(err)
	}
}

func TestConcat(t *testing.T) {

	if err := expect(Concat([]string{}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{""}), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Concat([]string{"a", "b", "c"}), "abc"); err != nil {
		t.Error(err)
	}
}

func TestJoin(t *testing.T) {

	if err := expect(Join([]string{}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{""}, "-"), ""); err != nil {
		t.Error(err)
	}

	if err := expect(Join([]string{"a", "b", "c"}, "-"), "a-b-c"); err != nil {
		t.Error(err)
	}
}

func toPointer(s []string) *[]string {
	return &s
}

func TestCut(t *testing.T) {

	s := []string{}
	u := []string{}
	t.Logf("1%#v:", toPointer(s))
	t.Logf("2%#v:", toPointer(u))

	if err := expect(Cut([]string{}, 0, -1), []string{}); err != nil {
		t.Error(err)
	}

	if err := expect(Cut([]string{}, 0, -1), []string{""}); err != nil {
		t.Error(err)
	}

	// if err := expect(Cut([]string{""}, "-"), ""); err != nil {
	// 	t.Error(err)
	// }

	// if err := expect(Cut([]string{"a", "b", "c"}, "-"), "a-b-c"); err != nil {
	// 	t.Error(err)
	// }
}

// func BenchmarkStringsReplaceAll(b *testing.B) {
// 	for i := 0; i < b.N; i++ {
// 		strings.Replace(TestFileContent, ToFind, ToReplace, -1)
// 	}
// }
<|endoftext|>


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

package stun

import (
	"errors"
	"net"
	"strconv"
)

type Client struct {
	serverAddr   string
	softwareName string
	conn         net.PacketConn
}

// NewClient returns a client without network connection. The network
// connection will be build when calling Discover function.
func NewClient() *Client {
	c := new(Client)
	c.SetSoftwareName(DefaultSoftwareName)
	return c
}

// NewClientWithConnection returns a client which uses the given connection.
// Please note the connection should be acquired via net.Listen* method.
func NewClientWithConnection(conn net.PacketConn) *Client {
	c := new(Client)
	c.conn = conn
	c.SetSoftwareName(DefaultSoftwareName)
	return c
}

// SetServerHost allows user to set the STUN hostname and port.
func (c *Client) SetServerHost(host string, port int) error {
	ips, err := net.LookupHost(host)
	if err != nil {
		return err
	}
	if len(ips) == 0 {
		return errors.New("Failed to get IP address of " + host + ".")
	}
	c.serverAddr = net.JoinHostPort(ips[0], strconv.Itoa(port))
	return nil
}

// SetServerAddr allows user to set the transport layer STUN server address.
func (c *Client) SetServerAddr(address string) {
	c.serverAddr = address
}

// SetSoftwareName allows user to set the name of the software, which is used
// for logging purpose (NOT used in the current implementation).
func (c *Client) SetSoftwareName(name string) {
	c.softwareName = name
}

// Discover contacts the STUN server and gets the response of NAT type, host
// for UDP punching.
func (c *Client) Discover() (NATType, *Host, error) {
	if c.serverAddr == "" {
		err := c.SetServerHost(DefaultServerHost, DefaultServerPort)
		if err != nil {
			return NAT_ERROR, nil, err
		}
	}

	addr, err := net.ResolveUDPAddr("udp", c.serverAddr)
	if err != nil {
		return NAT_ERROR, nil, err
	}

	conn, err := net.ListenUDP("udp", nil)
	if err != nil {
		return NAT_ERROR, nil, err
	}
	c.conn = conn
	defer conn.Close()

	return discover(c.conn, addr, c.softwareName)
}
<commit_msg>fix second time calling discover bug again<commit_after>// Copyright 2013, Cong Ding. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Author: Cong Ding <dinggnu@gmail.com>

package stun

import (
	"errors"
	"net"
	"strconv"
)

type Client struct {
	serverAddr   string
	softwareName string
	conn         net.PacketConn
}

// NewClient returns a client without network connection. The network
// connection will be build when calling Discover function.
func NewClient() *Client {
	c := new(Client)
	c.SetSoftwareName(DefaultSoftwareName)
	return c
}

// NewClientWithConnection returns a client which uses the given connection.
// Please note the connection should be acquired via net.Listen* method.
func NewClientWithConnection(conn net.PacketConn) *Client {
	c := new(Client)
	c.conn = conn
	c.SetSoftwareName(DefaultSoftwareName)
	return c
}

// SetServerHost allows user to set the STUN hostname and port.
func (c *Client) SetServerHost(host string, port int) error {
	ips, err := net.LookupHost(host)
	if err != nil {
		return err
	}
	if len(ips) == 0 {
		return errors.New("Failed to get IP address of " + host + ".")
	}
	c.serverAddr = net.JoinHostPort(ips[0], strconv.Itoa(port))
	return nil
}

// SetServerAddr allows user to set the transport layer STUN server address.
func (c *Client) SetServerAddr(address string) {
	c.serverAddr = address
}

// SetSoftwareName allows user to set the name of the software, which is used
// for logging purpose (NOT used in the current implementation).
func (c *Client) SetSoftwareName(name string) {
	c.softwareName = name
}

// Discover contacts the STUN server and gets the response of NAT type, host
// for UDP punching.
func (c *Client) Discover() (NATType, *Host, error) {
	if c.serverAddr == "" {
		err := c.SetServerHost(DefaultServerHost, DefaultServerPort)
		if err != nil {
			return NAT_ERROR, nil, err
		}
	}
	addr, err := net.ResolveUDPAddr("udp", c.serverAddr)
	if err != nil {
		return NAT_ERROR, nil, err
	}
	if c.conn == nil {
		conn, err := net.ListenUDP("udp", nil)
		if err != nil {
			return NAT_ERROR, nil, err
		}
		c.conn = conn
	}
	return discover(c.conn, addr, c.softwareName)
}
<|endoftext|>


<commit_before>package suite

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime/debug"
	"sync"
	"testing"

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

var allTestsFilter = func(_, _ string) (bool, error) { return true, nil }
var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run")

// Suite is a basic testing suite with methods for storing and
// retrieving the current *testing.T context.
type Suite struct {
	*assert.Assertions
	require *require.Assertions
	t       *testing.T
}

// T retrieves the current *testing.T context.
func (suite *Suite) T() *testing.T {
	return suite.t
}

// SetT sets the current *testing.T context.
func (suite *Suite) SetT(t *testing.T) {
	suite.t = t
	suite.Assertions = assert.New(t)
	suite.require = require.New(t)
}

// Require returns a require context for suite.
func (suite *Suite) Require() *require.Assertions {
	if suite.require == nil {
		suite.require = require.New(suite.T())
	}
	return suite.require
}

// Assert returns an assert context for suite.  Normally, you can call
// `suite.NoError(expected, actual)`, but for situations where the embedded
// methods are overridden (for example, you might want to override
// assert.Assertions with require.Assertions), this method is provided so you
// can call `suite.Assert().NoError()`.
func (suite *Suite) Assert() *assert.Assertions {
	if suite.Assertions == nil {
		suite.Assertions = assert.New(suite.T())
	}
	return suite.Assertions
}

func failOnPanic(t *testing.T) {
	r := recover()
	if r != nil {
		t.Errorf("test panicked: %v\n%s", r, debug.Stack())
		t.FailNow()
	}
}

// Run provides suite functionality around golang subtests.  It should be
// called in place of t.Run(name, func(t *testing.T)) in test suite code.
// The passed-in func will be executed as a subtest with a fresh instance of t.
// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
func (suite *Suite) Run(name string, subtest func()) bool {
	oldT := suite.T()
	defer suite.SetT(oldT)
	return oldT.Run(name, func(t *testing.T) {
		suite.SetT(t)
		subtest()
	})
}

// Run takes a testing suite and runs all of the tests attached
// to it.
func Run(t *testing.T, suite TestingSuite) {
	testsSync := &sync.WaitGroup{}
	suite.SetT(t)
	defer failOnPanic(t)

	suiteSetupDone := false

	methodFinder := reflect.TypeOf(suite)
	tests := []testing.InternalTest{}
	for index := 0; index < methodFinder.NumMethod(); index++ {
		method := methodFinder.Method(index)
		ok, err := methodFilter(method.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
			os.Exit(1)
		}
		if !ok {
			continue
		}
		if !suiteSetupDone {
			if setupAllSuite, ok := suite.(SetupAllSuite); ok {
				setupAllSuite.SetupSuite()
			}
			defer func() {
				if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
					testsSync.Wait()
					tearDownAllSuite.TearDownSuite()
				}
			}()
			suiteSetupDone = true
		}
		test := testing.InternalTest{
			Name: method.Name,
			F: func(t *testing.T) {
				defer testsSync.Done()
				parentT := suite.T()
				suite.SetT(t)
				defer failOnPanic(t)

				if setupTestSuite, ok := suite.(SetupTestSuite); ok {
					setupTestSuite.SetupTest()
				}
				if beforeTestSuite, ok := suite.(BeforeTest); ok {
					beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name)
				}
				defer func() {
					if afterTestSuite, ok := suite.(AfterTest); ok {
						afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name)
					}
					if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
						tearDownTestSuite.TearDownTest()
					}
					suite.SetT(parentT)
				}()
				method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
			},
		}
		tests = append(tests, test)
		testsSync.Add(1)
	}
	runTests(t, tests)
}

func runTests(t testing.TB, tests []testing.InternalTest) {
	r, ok := t.(runner)
	if !ok { // backwards compatibility with Go 1.6 and below
		if !testing.RunTests(allTestsFilter, tests) {
			t.Fail()
		}
		return
	}

	for _, test := range tests {
		r.Run(test.Name, test.F)
	}
}

// Filtering method according to set regular expression
// specified command-line argument -m
func methodFilter(name string) (bool, error) {
	if ok, _ := regexp.MatchString("^Test", name); !ok {
		return false, nil
	}
	return regexp.MatchString(*matchMethod, name)
}

type runner interface {
	Run(name string, f func(t *testing.T)) bool
}
<commit_msg>changed dependency to interface, s.t. consumers can easily mock the testing.T type<commit_after>package suite

import (
	"flag"
	"fmt"
	"os"
	"reflect"
	"regexp"
	"runtime/debug"
	"sync"
	"testing"

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

var allTestsFilter = func(_, _ string) (bool, error) { return true, nil }
var matchMethod = flag.String("testify.m", "", "regular expression to select tests of the testify suite to run")

type TestingT interface {
	Run(name string, f func(t *testing.T)) bool
	Helper()

	Errorf(format string, args ...interface{})
	FailNow()
}

// Suite is a basic testing suite with methods for storing and
// retrieving the current *testing.T context.
type Suite struct {
	*assert.Assertions
	require *require.Assertions
	t       TestingT
}

// T retrieves the current *testing.T context.
func (suite *Suite) T() TestingT {
	return suite.t
}

// SetT sets the current *testing.T context.
func (suite *Suite) SetT(t TestingT) {
	suite.t = t
	suite.Assertions = assert.New(t)
	suite.require = require.New(t)
}

// Require returns a require context for suite.
func (suite *Suite) Require() *require.Assertions {
	if suite.require == nil {
		suite.require = require.New(suite.T())
	}
	return suite.require
}

// Assert returns an assert context for suite.  Normally, you can call
// `suite.NoError(expected, actual)`, but for situations where the embedded
// methods are overridden (for example, you might want to override
// assert.Assertions with require.Assertions), this method is provided so you
// can call `suite.Assert().NoError()`.
func (suite *Suite) Assert() *assert.Assertions {
	if suite.Assertions == nil {
		suite.Assertions = assert.New(suite.T())
	}
	return suite.Assertions
}

func failOnPanic(t *testing.T) {
	r := recover()
	if r != nil {
		t.Errorf("test panicked: %v\n%s", r, debug.Stack())
		t.FailNow()
	}
}

// Run provides suite functionality around golang subtests.  It should be
// called in place of t.Run(name, func(t *testing.T)) in test suite code.
// The passed-in func will be executed as a subtest with a fresh instance of t.
// Provides compatibility with go test pkg -run TestSuite/TestName/SubTestName.
func (suite *Suite) Run(name string, subtest func()) bool {
	oldT := suite.T()
	defer suite.SetT(oldT)
	return oldT.Run(name, func(t *testing.T) {
		suite.SetT(t)
		subtest()
	})
}

// Run takes a testing suite and runs all of the tests attached
// to it.
func Run(t *testing.T, suite TestingSuite) {
	testsSync := &sync.WaitGroup{}
	suite.SetT(t)
	defer failOnPanic(t)

	suiteSetupDone := false

	methodFinder := reflect.TypeOf(suite)
	tests := []testing.InternalTest{}
	for index := 0; index < methodFinder.NumMethod(); index++ {
		method := methodFinder.Method(index)
		ok, err := methodFilter(method.Name)
		if err != nil {
			fmt.Fprintf(os.Stderr, "testify: invalid regexp for -m: %s\n", err)
			os.Exit(1)
		}
		if !ok {
			continue
		}
		if !suiteSetupDone {
			if setupAllSuite, ok := suite.(SetupAllSuite); ok {
				setupAllSuite.SetupSuite()
			}
			defer func() {
				if tearDownAllSuite, ok := suite.(TearDownAllSuite); ok {
					testsSync.Wait()
					tearDownAllSuite.TearDownSuite()
				}
			}()
			suiteSetupDone = true
		}
		test := testing.InternalTest{
			Name: method.Name,
			F: func(t *testing.T) {
				defer testsSync.Done()
				parentT := suite.T()
				suite.SetT(t)
				defer failOnPanic(t)

				if setupTestSuite, ok := suite.(SetupTestSuite); ok {
					setupTestSuite.SetupTest()
				}
				if beforeTestSuite, ok := suite.(BeforeTest); ok {
					beforeTestSuite.BeforeTest(methodFinder.Elem().Name(), method.Name)
				}
				defer func() {
					if afterTestSuite, ok := suite.(AfterTest); ok {
						afterTestSuite.AfterTest(methodFinder.Elem().Name(), method.Name)
					}
					if tearDownTestSuite, ok := suite.(TearDownTestSuite); ok {
						tearDownTestSuite.TearDownTest()
					}
					suite.SetT(parentT)
				}()
				method.Func.Call([]reflect.Value{reflect.ValueOf(suite)})
			},
		}
		tests = append(tests, test)
		testsSync.Add(1)
	}
	runTests(t, tests)
}

func runTests(t testing.TB, tests []testing.InternalTest) {
	r, ok := t.(runner)
	if !ok { // backwards compatibility with Go 1.6 and below
		if !testing.RunTests(allTestsFilter, tests) {
			t.Fail()
		}
		return
	}

	for _, test := range tests {
		r.Run(test.Name, test.F)
	}
}

// Filtering method according to set regular expression
// specified command-line argument -m
func methodFilter(name string) (bool, error) {
	if ok, _ := regexp.MatchString("^Test", name); !ok {
		return false, nil
	}
	return regexp.MatchString(*matchMethod, name)
}

type runner interface {
	Run(name string, f func(t *testing.T)) bool
}
<|endoftext|>


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

// Package suite allows to read tests and collections of tests (suites) from
// disk and execute them in a controlled way or run throughput load test from
// these test/suites.
//
package suite

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"sort"
	"strings"
	"time"

	"github.com/vdobler/ht/cookiejar"
	"github.com/vdobler/ht/ht"
	"github.com/vdobler/ht/mock"
	"github.com/vdobler/ht/scope"
)

// A Suite is a collection of Tests which can be executed sequentily with the
// result captured.
type Suite struct {
	Name        string // Name of the Suite.
	Description string // Description of what's going on here.
	KeepCookies bool   // KeepCookies in a cookie jar common to all Tests.

	Status   ht.Status     // Status is the overall status of the whole suite.
	Error    error         // Error encountered during execution of the suite.
	Started  time.Time     // Start of the execution.
	Duration time.Duration // Duration of the execution.

	Tests []*ht.Test // The Tests to execute

	Variables      scope.Variables // The initial variable assignment
	FinalVariables scope.Variables // The final set of variables.
	Jar            *cookiejar.Jar  // The cookie jar used

	Verbosity int
	Log       interface {
		Printf(format string, a ...interface{})
	}

	globals          scope.Variables
	tests            []*RawTest
	noneTeardownTest int
}

func shouldRun(t int, rs *RawSuite, s *Suite) bool {
	if !rs.tests[t].IsEnabled() {
		return false
	}

	// Stop execution on errors during setup
	for i := 0; i < len(rs.Setup) && i < len(s.Tests); i++ {
		if s.Tests[i].Status > ht.Pass {
			return false
		}
	}
	return true
}

// NewFromRaw sets up a new Suite from rs, read to be Iterated.
func NewFromRaw(rs *RawSuite, global map[string]string, jar *cookiejar.Jar, logger *log.Logger) *Suite {
	// Create cookie jar if needed.
	if rs.KeepCookies {
		if jar == nil {
			// Make own, private-use jar.
			jar, _ = cookiejar.New(nil)
		}
	} else {
		jar = nil
	}

	if logger == nil {
		logger = log.New(ioutil.Discard, "", 0)
	}

	suite := &Suite{
		KeepCookies: rs.KeepCookies,

		Status: ht.NotRun,
		Error:  nil,

		Tests: make([]*ht.Test, 0, len(rs.tests)),

		Variables:        make(map[string]string),
		FinalVariables:   make(map[string]string),
		Jar:              jar,
		Log:              logger,
		Verbosity:        rs.Verbosity,
		tests:            rs.tests,
		noneTeardownTest: len(rs.Setup) + len(rs.Main),
	}

	suite.globals = scope.New(global, rs.Variables, true)
	suite.globals["SUITE_DIR"] = rs.File.Dirname()
	suite.globals["SUITE_NAME"] = rs.File.Basename()
	replacer := suite.globals.Replacer()

	suite.Name = replacer.Replace(rs.Name)
	suite.Description = replacer.Replace(rs.Description)

	for n, v := range suite.globals {
		suite.Variables[n] = v
	}

	return suite
}

// A Executor is responsible for executing the given test during the
// Iterate'ion of a Suite. It should return nil if execution should continue
// and ErrAbortExecution to stop further iteration.
type Executor func(test *ht.Test) error

var (
	// ErrAbortExecution indicates that suite iteration should stop.
	ErrAbortExecution = errors.New("Abort Execution")
)

var mockDelay = 50 * time.Millisecond

// Iterate the suite through the given executor.
func (suite *Suite) Iterate(executor Executor) {
	now := time.Now()
	now = now.Add(-time.Duration(now.Nanosecond()))
	suite.Started = now

	overall := ht.NotRun
	errors := ht.ErrorList{}

	for _, rt := range suite.tests {
		// suite.Log.Printf("Executing Test %q\n", rt.File.Name)
		callScope := scope.New(suite.globals, rt.contextVars, true)
		testScope := scope.New(callScope, rt.Variables, false)
		testScope["TEST_DIR"] = rt.File.Dirname()
		testScope["TEST_NAME"] = rt.File.Basename()
		test, err := rt.ToTest(testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}
		test.Jar = suite.Jar
		test.Log = suite.Log

		// Mocks requested for this test: We expect each mock to be
		// called exactly once (and this call should pass).
		var mockResult []*ht.Test
		stopMocks, monitor, monitoringDone, expect, err := startMocks(suite, test, rt, &mockResult, testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}

		// Execute the test (if not bogus).
		exstat := executor(test)

		if stopMocks != nil {
			// We got running mocks: Stop mock handling and stop monitoring
			stopMocks <- true
			<-stopMocks
			close(monitor)
			<-monitoringDone

			// Analyse what we got and updates test.
			analyseMocks(test, mockResult, expect)
		}
		if test.Status == ht.Pass {
			suite.updateVariables(test)
		}

		suite.Tests = append(suite.Tests, test)
		if test.Status > overall {
			overall = test.Status
		}
		if err := test.Error; err != nil {
			errors = append(errors, err)
		}

		if exstat == ErrAbortExecution {
			break
		}
	}
	suite.Duration = time.Since(suite.Started)
	clip := suite.Duration.Nanoseconds() % 1000000
	suite.Duration -= time.Duration(clip)
	suite.Status = overall
	if len(errors) == 0 {
		suite.Error = nil
	} else {
		suite.Error = errors
	}

	for n, v := range suite.globals {
		suite.FinalVariables[n] = v
	}
}

func startMocks(suite *Suite, test *ht.Test, rt *RawTest, mockResult *[]*ht.Test, testScope scope.Variables) (stopMocks chan bool, monitor chan *ht.Test, monitoringDone chan bool, expect []string, err error) {
	if len(rt.mocks) == 0 {
		return nil, nil, nil, nil, nil
	}

	monitor = make(chan *ht.Test)
	mocks := []*mock.Mock{}
	for i, m := range rt.mocks {
		mockScope := scope.New(testScope, rt.Variables, false)
		mockScope["MOCK_DIR"] = m.Dirname()
		mockScope["MOCK_NAME"] = m.Basename()
		mk, err := m.ToMock(mockScope, true)
		if err != nil {
			return nil, nil, nil, nil,
				fmt.Errorf("mock %d %q is malformed: %s",
					i+1, m.Name, err)

		}
		mk.Monitor = monitor
		mocks = append(mocks, mk)
		expect = append(expect, mk.Name)
	}

	// TODO: handle 404
	stopMocks, err = mock.Serve(mocks, nil, suite.Log)
	if err != nil {
		return nil, nil, nil, nil, err
	}

	monitoringDone = make(chan bool)
	go func() {
		for report := range monitor {
			logMock(suite, report)
			*mockResult = append(*mockResult, report)
		}
		close(monitoringDone)
	}()
	time.Sleep(mockDelay) // I'm clueless...

	return stopMocks, monitor, monitoringDone, expect, nil
}

func analyseMocks(test *ht.Test, mockResult []*ht.Test, expect []string) {
	actual := []string{}
	for _, mt := range mockResult {
		actual = append(actual, mt.Name)
		// Propagete state of mock invocations to main test.
		if mt.Status > test.Status {
			test.Status = mt.Status
			test.Error = mt.Error
		}
		// Augment check name with information about mock.
		for i := range mt.CheckResults {
			name := mt.CheckResults[i].Name
			name = fmt.Sprintf("Request to mock %q: %s", mt.Name, name)
			mt.CheckResults[i].Name = name
		}
		// Append check results of mock to main test checks.
		test.CheckResults = append(test.CheckResults, mt.CheckResults...)
	}

	// Did we get exactly what we expected?
	sort.Strings(expect)
	sort.Strings(actual)
	// Sorry for that.
	want, got := strings.Join(expect, " ※ "), strings.Join(actual, " ※ ")
	if got != want && test.Status == ht.Pass {
		test.Status = ht.Fail
		test.Error = fmt.Errorf("Expected mocks [%s] but actual mock invocations were [%s]",
			want, got)
	}
}

func logMock(suite *Suite, report *ht.Test) {
	if suite.Verbosity <= 0 {
		return
	}
	full := ""
	if suite.Verbosity >= 3 {
		full = "\n"
		full += fmt.Sprintf("  Request\n    Header\n")
		for k, v := range report.Request.Request.Header {
			full += fmt.Sprintf("      %s: %s\n", k, v)
		}
		full += fmt.Sprintf("    Body\n")
		full += fmt.Sprintf("      %s\n", report.Request.SentBody)
		full += fmt.Sprintf("  Response\n    Header\n")
		for k, v := range report.Response.Response.Header {
			full += fmt.Sprintf("      %s: %s\n", k, v)
		}
		full += fmt.Sprintf("    Body\n")
		full += fmt.Sprintf("      %s\n", report.Response.BodyStr)
		full += fmt.Sprintf("========================================================\n")

	}
	suite.Log.Printf("Mock invoked %q: %s %s%s", report.Name,
		report.Request.Method, report.Request.URL, full)

}

func (suite *Suite) updateVariables(test *ht.Test) {
	if test.Status != ht.Pass {
		return
	}

	for varname, value := range test.Extract() {
		if suite.Verbosity >= 2 {
			if old, ok := suite.globals[varname]; ok {
				if value != old {
					suite.Log.Printf("Updating variable %q to %q\n",
						varname, value)
				} else {
					suite.Log.Printf("Keeping  variable %q as %q\n",
						varname, value)
				}
			} else {
				suite.Log.Printf("Setting  variable %q to %q\n",
					varname, value)
			}
		}

		suite.globals[varname] = value
	}
}

func (suite *Suite) updateStatus(test *ht.Test) {
	if test.Status <= suite.Status {
		return
	}

	suite.Status = test.Status
	if test.Error != nil {
		suite.Error = test.Error
	}
}

// Stats counts the test results of s.
func (suite *Suite) Stats() (notRun int, skipped int, passed int, failed int, errored int, bogus int) {
	for _, tr := range suite.Tests {
		switch tr.Status {
		case ht.NotRun:
			notRun++
		case ht.Skipped:
			skipped++
		case ht.Pass:
			passed++
		case ht.Fail:
			failed++
		case ht.Error:
			errored++
		case ht.Bogus:
			bogus++
		default:
			panic(fmt.Sprintf("No such Status %d in suite %q test %q",
				tr.Status, suite.Name, tr.Name))
		}
	}
	return
}
<commit_msg>suite: report misguided calls to mock servervices<commit_after>// Copyright 2016 Volker Dobler.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package suite allows to read tests and collections of tests (suites) from
// disk and execute them in a controlled way or run throughput load test from
// these test/suites.
//
package suite

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strings"
	"time"

	"github.com/vdobler/ht/cookiejar"
	"github.com/vdobler/ht/ht"
	"github.com/vdobler/ht/mock"
	"github.com/vdobler/ht/scope"
)

// A Suite is a collection of Tests which can be executed sequentily with the
// result captured.
type Suite struct {
	Name        string // Name of the Suite.
	Description string // Description of what's going on here.
	KeepCookies bool   // KeepCookies in a cookie jar common to all Tests.

	Status   ht.Status     // Status is the overall status of the whole suite.
	Error    error         // Error encountered during execution of the suite.
	Started  time.Time     // Start of the execution.
	Duration time.Duration // Duration of the execution.

	Tests []*ht.Test // The Tests to execute

	Variables      scope.Variables // The initial variable assignment
	FinalVariables scope.Variables // The final set of variables.
	Jar            *cookiejar.Jar  // The cookie jar used

	Verbosity int
	Log       interface {
		Printf(format string, a ...interface{})
	}

	globals          scope.Variables
	tests            []*RawTest
	noneTeardownTest int
}

func shouldRun(t int, rs *RawSuite, s *Suite) bool {
	if !rs.tests[t].IsEnabled() {
		return false
	}

	// Stop execution on errors during setup
	for i := 0; i < len(rs.Setup) && i < len(s.Tests); i++ {
		if s.Tests[i].Status > ht.Pass {
			return false
		}
	}
	return true
}

// NewFromRaw sets up a new Suite from rs, read to be Iterated.
func NewFromRaw(rs *RawSuite, global map[string]string, jar *cookiejar.Jar, logger *log.Logger) *Suite {
	// Create cookie jar if needed.
	if rs.KeepCookies {
		if jar == nil {
			// Make own, private-use jar.
			jar, _ = cookiejar.New(nil)
		}
	} else {
		jar = nil
	}

	if logger == nil {
		logger = log.New(ioutil.Discard, "", 0)
	}

	suite := &Suite{
		KeepCookies: rs.KeepCookies,

		Status: ht.NotRun,
		Error:  nil,

		Tests: make([]*ht.Test, 0, len(rs.tests)),

		Variables:        make(map[string]string),
		FinalVariables:   make(map[string]string),
		Jar:              jar,
		Log:              logger,
		Verbosity:        rs.Verbosity,
		tests:            rs.tests,
		noneTeardownTest: len(rs.Setup) + len(rs.Main),
	}

	suite.globals = scope.New(global, rs.Variables, true)
	suite.globals["SUITE_DIR"] = rs.File.Dirname()
	suite.globals["SUITE_NAME"] = rs.File.Basename()
	replacer := suite.globals.Replacer()

	suite.Name = replacer.Replace(rs.Name)
	suite.Description = replacer.Replace(rs.Description)

	for n, v := range suite.globals {
		suite.Variables[n] = v
	}

	return suite
}

// A Executor is responsible for executing the given test during the
// Iterate'ion of a Suite. It should return nil if execution should continue
// and ErrAbortExecution to stop further iteration.
type Executor func(test *ht.Test) error

var (
	// ErrAbortExecution indicates that suite iteration should stop.
	ErrAbortExecution = errors.New("Abort Execution")
)

var mockDelay = 50 * time.Millisecond

// Iterate the suite through the given executor.
func (suite *Suite) Iterate(executor Executor) {
	now := time.Now()
	now = now.Add(-time.Duration(now.Nanosecond()))
	suite.Started = now

	overall := ht.NotRun
	errors := ht.ErrorList{}

	for _, rt := range suite.tests {
		// suite.Log.Printf("Executing Test %q\n", rt.File.Name)
		callScope := scope.New(suite.globals, rt.contextVars, true)
		testScope := scope.New(callScope, rt.Variables, false)
		testScope["TEST_DIR"] = rt.File.Dirname()
		testScope["TEST_NAME"] = rt.File.Basename()
		test, err := rt.ToTest(testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}
		test.Jar = suite.Jar
		test.Log = suite.Log

		// Mocks requested for this test: We expect each mock to be
		// called exactly once (and this call should pass).
		var mockResult []*ht.Test
		stopMocks, monitor, monitoringDone, expect, err := startMocks(suite, test, rt, &mockResult, testScope)
		if err != nil {
			test.Status = ht.Bogus
			test.Error = err
		}

		// Execute the test (if not bogus).
		exstat := executor(test)

		if stopMocks != nil {
			// We got running mocks: Stop mock handling and stop monitoring
			stopMocks <- true
			<-stopMocks
			close(monitor)
			<-monitoringDone

			// Analyse what we got and updates test.
			analyseMocks(test, mockResult, expect)
		}
		if test.Status == ht.Pass {
			suite.updateVariables(test)
		}

		suite.Tests = append(suite.Tests, test)
		if test.Status > overall {
			overall = test.Status
		}
		if err := test.Error; err != nil {
			errors = append(errors, err)
		}

		if exstat == ErrAbortExecution {
			break
		}
	}
	suite.Duration = time.Since(suite.Started)
	clip := suite.Duration.Nanoseconds() % 1000000
	suite.Duration -= time.Duration(clip)
	suite.Status = overall
	if len(errors) == 0 {
		suite.Error = nil
	} else {
		suite.Error = errors
	}

	for n, v := range suite.globals {
		suite.FinalVariables[n] = v
	}
}

func startMocks(suite *Suite, test *ht.Test, rt *RawTest, mockResult *[]*ht.Test, testScope scope.Variables) (stopMocks chan bool, monitor chan *ht.Test, monitoringDone chan bool, expect []string, err error) {
	if len(rt.mocks) == 0 {
		return nil, nil, nil, nil, nil
	}

	monitor = make(chan *ht.Test)
	mocks := []*mock.Mock{}
	for i, m := range rt.mocks {
		mockScope := scope.New(testScope, rt.Variables, false)
		mockScope["MOCK_DIR"] = m.Dirname()
		mockScope["MOCK_NAME"] = m.Basename()
		mk, err := m.ToMock(mockScope, true)
		if err != nil {
			return nil, nil, nil, nil,
				fmt.Errorf("mock %d %q is malformed: %s",
					i+1, m.Name, err)

		}
		mk.Monitor = monitor
		mocks = append(mocks, mk)
		expect = append(expect, mk.Name)
	}

	// Report any calls that miss explicit mock handlers as 404.
	notFoundHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		body, _ := ioutil.ReadAll(r.Body)
		u := r.URL.String()
		report := &ht.Test{
			Name: "Not Found " + u,
			Request: ht.Request{
				Method:   r.Method,
				URL:      u,
				Header:   r.Header,
				Request:  r,
				SentBody: string(body),
			},
		}
		http.Error(w, "No mock for "+u, http.StatusNotFound)
		monitor <- report
	})

	stopMocks, err = mock.Serve(mocks, notFoundHandler, suite.Log)
	if err != nil {
		return nil, nil, nil, nil, err
	}

	monitoringDone = make(chan bool)
	go func() {
		for report := range monitor {
			logMock(suite, report)
			*mockResult = append(*mockResult, report)
		}
		close(monitoringDone)
	}()
	time.Sleep(mockDelay) // I'm clueless...

	return stopMocks, monitor, monitoringDone, expect, nil
}

func analyseMocks(test *ht.Test, mockResult []*ht.Test, expect []string) {
	actual := []string{}
	for _, mt := range mockResult {
		actual = append(actual, mt.Name)
		// Propagete state of mock invocations to main test.
		if mt.Status > test.Status {
			test.Status = mt.Status
			test.Error = mt.Error
		}
		// Augment check name with information about mock.
		for i := range mt.CheckResults {
			name := mt.CheckResults[i].Name
			name = fmt.Sprintf("Request to mock %q: %s", mt.Name, name)
			mt.CheckResults[i].Name = name
		}
		// Append check results of mock to main test checks.
		test.CheckResults = append(test.CheckResults, mt.CheckResults...)
	}

	// Did we get exactly what we expected?
	sort.Strings(expect)
	sort.Strings(actual)
	// Sorry for that.
	want, got := strings.Join(expect, " ※ "), strings.Join(actual, " ※ ")
	if got != want && test.Status == ht.Pass {
		test.Status = ht.Fail
		test.Error = fmt.Errorf("Expected mocks [%s] but actual mock invocations were [%s]",
			want, got)
	}
}

func logMock(suite *Suite, report *ht.Test) {
	if suite.Verbosity <= 0 {
		return
	}
	full := ""
	if suite.Verbosity >= 3 {
		full = "\n"
		full += fmt.Sprintf("  Request\n    Header\n")
		for k, v := range report.Request.Request.Header {
			full += fmt.Sprintf("      %s: %s\n", k, v)
		}
		full += fmt.Sprintf("    Body\n")
		full += fmt.Sprintf("      %s\n", report.Request.SentBody)
		full += fmt.Sprintf("  Response\n    Header\n")
		for k, v := range report.Response.Response.Header {
			full += fmt.Sprintf("      %s: %s\n", k, v)
		}
		full += fmt.Sprintf("    Body\n")
		full += fmt.Sprintf("      %s\n", report.Response.BodyStr)
		full += fmt.Sprintf("========================================================\n")

	}
	suite.Log.Printf("Mock invoked %q: %s %s%s", report.Name,
		report.Request.Method, report.Request.URL, full)

}

func (suite *Suite) updateVariables(test *ht.Test) {
	if test.Status != ht.Pass {
		return
	}

	for varname, value := range test.Extract() {
		if suite.Verbosity >= 2 {
			if old, ok := suite.globals[varname]; ok {
				if value != old {
					suite.Log.Printf("Updating variable %q to %q\n",
						varname, value)
				} else {
					suite.Log.Printf("Keeping  variable %q as %q\n",
						varname, value)
				}
			} else {
				suite.Log.Printf("Setting  variable %q to %q\n",
					varname, value)
			}
		}

		suite.globals[varname] = value
	}
}

func (suite *Suite) updateStatus(test *ht.Test) {
	if test.Status <= suite.Status {
		return
	}

	suite.Status = test.Status
	if test.Error != nil {
		suite.Error = test.Error
	}
}

// Stats counts the test results of s.
func (suite *Suite) Stats() (notRun int, skipped int, passed int, failed int, errored int, bogus int) {
	for _, tr := range suite.Tests {
		switch tr.Status {
		case ht.NotRun:
			notRun++
		case ht.Skipped:
			skipped++
		case ht.Pass:
			passed++
		case ht.Fail:
			failed++
		case ht.Error:
			errored++
		case ht.Bogus:
			bogus++
		default:
			panic(fmt.Sprintf("No such Status %d in suite %q test %q",
				tr.Status, suite.Name, tr.Name))
		}
	}
	return
}
<|endoftext|>