<commit_before>package bucket

import (
	"bytes"
	"fmt"
	"strings"
	"time"

	"github.com/daidokoro/qaz/logger"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

// -- Contains all things S3

// Log define logger
var Log *logger.Logger

// S3Read - Reads the content of a given s3 url endpoint and returns the content string.
func S3Read(url string, sess *session.Session) (string, error) {
	svc := s3.New(sess)

	// Parse s3 url
	bucket := strings.Split(strings.Replace(strings.ToLower(url), `s3://`, "", -1), `/`)[0]
	key := strings.Replace(strings.ToLower(url), fmt.Sprintf("s3://%s/", bucket), "", -1)

	params := &s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	}

	Log.Debug(fmt.Sprintln("Calling S3 [GetObject] with parameters:", params))
	resp, err := svc.GetObject(params)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)

	Log.Debug("Reading from S3 Response Body")
	buf.ReadFrom(resp.Body)
	return buf.String(), nil

}

// S3write - Writes a file to s3 and returns the presigned url
func S3write(bucket string, key string, body string, sess *session.Session) (string, error) {
	svc := s3.New(sess)
	params := &s3.PutObjectInput{
		Bucket: &bucket,
		Key:    &key,
		Body:   bytes.NewReader([]byte(body)),
		Metadata: map[string]*string{
			"created_by": aws.String("qaz"),
		},
	}

	Log.Debug(fmt.Sprintln("Calling S3 [PutObject] with parameters:", params))
	_, err := svc.PutObject(params)
	if err != nil {
		return "", err
	}

	req, _ := svc.GetObjectRequest(&s3.GetObjectInput{
		Bucket: &bucket,
		Key:    &key,
	})

	url, err := req.Presign(10 * time.Minute)
	if err != nil {
		return "", err
	}

	return url, nil

}

// Create - create s3 bucket
func Create(bucket string, sess *session.Session) error {
	svc := s3.New(sess)

	params := &s3.CreateBucketInput{
		Bucket: &bucket,
	}

	Log.Debug(fmt.Sprintln("Calling S3 [CreateBucket] with parameters:", params))
	_, err := svc.CreateBucket(params)
	if err != nil {
		return err
	}

	if err := svc.WaitUntilBucketExists(&s3.HeadBucketInput{Bucket: aws.String(bucket)}); err != nil {
		return err
	}

	return nil

}

// Exists - checks if bucket exists - if err, then its assumed that the bucket does not exist.
func Exists(bucket string, sess *session.Session) (bool, error) {
	svc := s3.New(sess)
	params := &s3.HeadBucketInput{
		Bucket: &bucket,
	}

	Log.Debug(fmt.Sprintln("Calling S3 [HeadBucket] with parameters:", params))
	_, err := svc.HeadBucket(params)
	if err != nil {
		return false, err
	}

	return true, nil

}
<commit_msg>added support for go url lib<commit_after>package bucket

import (
	"bytes"
	"fmt"
	"net/url"
	"strings"
	"time"

	"github.com/daidokoro/qaz/logger"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
)

// -- Contains all things S3

// Log define logger
var Log *logger.Logger

// S3Read - Reads the content of a given s3 url endpoint and returns the content string.
func S3Read(URL string, sess *session.Session) (string, error) {
	svc := s3.New(sess)

	src, err := url.Parse(URL)
	if err != nil {
		return "", err
	}

	// Parse s3 url
	bucket := src.Scheme
	key := strings.TrimPrefix(src.Path, "/")

	params := &s3.GetObjectInput{
		Bucket: aws.String(bucket),
		Key:    aws.String(key),
	}

	Log.Debug(fmt.Sprintln("Calling S3 [GetObject] with parameters:", params))
	resp, err := svc.GetObject(params)
	if err != nil {
		return "", err
	}

	buf := new(bytes.Buffer)

	Log.Debug("Reading from S3 Response Body")
	buf.ReadFrom(resp.Body)
	return buf.String(), nil

}

// S3write - Writes a file to s3 and returns the presigned url
func S3write(bucket string, key string, body string, sess *session.Session) (string, error) {
	svc := s3.New(sess)
	params := &s3.PutObjectInput{
		Bucket: &bucket,
		Key:    &key,
		Body:   bytes.NewReader([]byte(body)),
		Metadata: map[string]*string{
			"created_by": aws.String("qaz"),
		},
	}

	Log.Debug(fmt.Sprintln("Calling S3 [PutObject] with parameters:", params))
	_, err := svc.PutObject(params)
	if err != nil {
		return "", err
	}

	req, _ := svc.GetObjectRequest(&s3.GetObjectInput{
		Bucket: &bucket,
		Key:    &key,
	})

	url, err := req.Presign(10 * time.Minute)
	if err != nil {
		return "", err
	}

	return url, nil

}

// Create - create s3 bucket
func Create(bucket string, sess *session.Session) error {
	svc := s3.New(sess)

	params := &s3.CreateBucketInput{
		Bucket: &bucket,
	}

	Log.Debug(fmt.Sprintln("Calling S3 [CreateBucket] with parameters:", params))
	_, err := svc.CreateBucket(params)
	if err != nil {
		return err
	}

	if err := svc.WaitUntilBucketExists(&s3.HeadBucketInput{Bucket: aws.String(bucket)}); err != nil {
		return err
	}

	return nil

}

// Exists - checks if bucket exists - if err, then its assumed that the bucket does not exist.
func Exists(bucket string, sess *session.Session) (bool, error) {
	svc := s3.New(sess)
	params := &s3.HeadBucketInput{
		Bucket: &bucket,
	}

	Log.Debug(fmt.Sprintln("Calling S3 [HeadBucket] with parameters:", params))
	_, err := svc.HeadBucket(params)
	if err != nil {
		return false, err
	}

	return true, nil

}
<|endoftext|>


<commit_before>package fetcher

import (
	"io/ioutil"
	"net/http"
	"time"

	"unicode/utf8"

	"github.com/advancedlogic/GoOse"
)

type Article struct {
	Url         string
	Title       string
	Description string
	Body        string
	StatusCode  int
}

func GetArticle(url string) Article {
	g := goose.New()
	timeout := time.Duration(5 * time.Second)
	client := http.Client{
		Timeout: timeout,
	}

	resp, err := client.Get(url)
	if err != nil {
		return Article{}
	}
	defer resp.Body.Close()

	html, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Article{StatusCode: resp.StatusCode}
	}

	if !utf8.Valid(html) {
		return Article{Url: resp.Request.URL.String(), StatusCode: resp.StatusCode}
	}

	article, err := g.ExtractFromRawHTML(url, string(html))
	if err != nil {
		return Article{StatusCode: resp.StatusCode}
	}
	return Article{resp.Request.URL.String(), article.Title, article.MetaDescription, article.CleanedText, resp.StatusCode}
}
<commit_msg>canonicalがあればそちらを優先する<commit_after>package fetcher

import (
	"io/ioutil"
	"net/http"
	"time"

	"unicode/utf8"

	"github.com/advancedlogic/GoOse"
)

type Article struct {
	Url         string
	Title       string
	Description string
	Body        string
	StatusCode  int
}

func GetArticle(url string) Article {
	g := goose.New()
	timeout := time.Duration(5 * time.Second)
	client := http.Client{
		Timeout: timeout,
	}

	resp, err := client.Get(url)
	if err != nil {
		return Article{}
	}
	defer resp.Body.Close()

	html, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return Article{StatusCode: resp.StatusCode}
	}

	if !utf8.Valid(html) {
		return Article{Url: resp.Request.URL.String(), StatusCode: resp.StatusCode}
	}

	article, err := g.ExtractFromRawHTML(url, string(html))
	if err != nil {
		return Article{StatusCode: resp.StatusCode}
	}

	finalUrl := article.CanonicalLink
	if finalUrl == "" {
		finalUrl = resp.Request.URL.String()
	}

	return Article{finalUrl, article.Title, article.MetaDescription, article.CleanedText, resp.StatusCode}
}
<|endoftext|>


<commit_before>package gocb

import (
	"encoding/json"
	"github.com/couchbase/gocb/gocbcore"
	"log"
)

type subDocResult struct {
	path string
	data []byte
	err  error
}

// Represents multiple chunks of a full Document.
type DocumentFragment struct {
	cas      Cas
	mt       MutationToken
	contents []subDocResult
	pathMap  map[string]int
}

// Returns the Cas of the Document
func (frag *DocumentFragment) Cas() Cas {
	return frag.cas
}

// Returns the MutationToken for the change represented by this DocumentFragment.
func (frag *DocumentFragment) MutationToken() MutationToken {
	return frag.mt
}

// Retrieve the value of the operation by its index. The index is the position of
// the operation as it was added to the builder.
func (frag *DocumentFragment) ContentByIndex(idx int, valuePtr interface{}) error {
	res := frag.contents[idx]
	if res.err != nil {
		return res.err
	}
	if valuePtr == nil {
		return nil
	}
	return json.Unmarshal(res.data, valuePtr)
}

// Retrieve the value of the operation by its path. The path is the path provided
// to the operation
func (frag *DocumentFragment) Content(path string, valuePtr interface{}) error {
	if frag.pathMap == nil {
		frag.pathMap = make(map[string]int)
		for i, v := range frag.contents {
			frag.pathMap[v.path] = i
		}
	}
	return frag.ContentByIndex(frag.pathMap[path], valuePtr)
}

// Checks whether the indicated path exists in this DocumentFragment and no
// errors were returned from the server.
func (frag *DocumentFragment) Exists(path string) bool {
	err := frag.Content(path, nil)
	return err == nil
}

// Builder used to create a set of sub-document lookup operations.
type LookupInBuilder struct {
	bucket *Bucket
	name   string
	ops    []gocbcore.SubDocOp
}

// Executes this set of lookup operations on the bucket.
func (set *LookupInBuilder) Execute() (*DocumentFragment, error) {
	return set.bucket.lookupIn(set)
}

// Indicate a path to be retrieved from the document.  The value of the path
// can later be retrieved (after .Execute()) using the Content or ContentByIndex
// method. The path syntax follows N1QL's path syntax (e.g. `foo.bar.baz`).
func (set *LookupInBuilder) Get(path string) *LookupInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpGet,
		Path: path,
	}
	set.ops = append(set.ops, op)
	return set
}

// Similar to Get(), but does not actually retrieve the value from the server.
// This may save bandwidth if you only need to check for the existence of a
// path (without caring for its content). You can check the status of this
// operation by using .Content (and ignoring the value) or .Exists()
func (set *LookupInBuilder) Exists(path string) *LookupInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpExists,
		Path: path,
	}
	set.ops = append(set.ops, op)
	return set
}

func (b *Bucket) lookupIn(set *LookupInBuilder) (resOut *DocumentFragment, errOut error) {
	signal := make(chan bool, 1)
	op, err := b.client.SubDocLookup([]byte(set.name), set.ops,
		func(results []gocbcore.SubDocResult, cas gocbcore.Cas, err error) {
			errOut = err

			{
				resSet := &DocumentFragment{}
				resSet.contents = make([]subDocResult, len(results))

				for i := range results {
					resSet.contents[i].path = set.ops[i].Path
					resSet.contents[i].err = results[i].Err
					if results[i].Value != nil {
						resSet.contents[i].data = append([]byte(nil), results[i].Value...)
					}
				}

				resOut = resSet
			}

			signal <- true
		})
	if err != nil {
		return nil, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, ErrTimeout
	}
}

// Creates a sub-document lookup operation builder.
func (b *Bucket) LookupIn(key string) *LookupInBuilder {
	return &LookupInBuilder{
		bucket: b,
		name:   key,
	}
}

// Builder used to create a set of sub-document mutation operations.
type MutateInBuilder struct {
	bucket *Bucket
	name   string
	cas    gocbcore.Cas
	expiry uint32
	ops    []gocbcore.SubDocOp
}

// Executes this set of mutation operations on the bucket.
func (set *MutateInBuilder) Execute() (*DocumentFragment, error) {
	return set.bucket.mutateIn(set)
}

// Adds an insert operation to this mutation operation set.
func (set *MutateInBuilder) Insert(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpDictAdd,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an upsert operation to this mutation operation set.
func (set *MutateInBuilder) Upsert(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpDictSet,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an replace operation to this mutation operation set.
func (set *MutateInBuilder) Replace(path string, value interface{}) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpReplace,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	set.ops = append(set.ops, op)
	return set
}

type subDocMultiValue []byte

func (set *MutateInBuilder) marshalArrayMulti(in interface{}) (out []byte) {
	out, err := json.Marshal(in)
	if err != nil {
		log.Panic(err)
	}

	// Assert first character is a '['
	if len(out) < 2 || out[0] != '[' {
		log.Panic("Not a JSON array")
	}

	out = out[1 : len(out)-1]
	return
}

// Adds an remove operation to this mutation operation set.
func (set *MutateInBuilder) Remove(path string) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpDelete,
		Path: path,
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an element to the beginning (i.e. left) of an array
func (set *MutateInBuilder) ArrayPrepend(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayPushFirst,
		Path: path,
	}
	if multiVal, isMulti := value.(subDocMultiValue); isMulti {
		op.Value = multiVal
	} else {
		op.Value, _ = json.Marshal(value)
	}
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an element to the end (i.e. right) of an array
func (set *MutateInBuilder) ArrayAppend(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayPushLast,
		Path: path,
	}
	if multiVal, isMulti := value.(subDocMultiValue); isMulti {
		op.Value = multiVal
	} else {
		op.Value, _ = json.Marshal(value)
	}
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Inserts an element at a given position within an array. The position should be
// specified as part of the path, e.g. path.to.array[3]
func (set *MutateInBuilder) ArrayInsert(path string, value interface{}) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayInsert,
		Path: path,
	}
	if multiVal, isMulti := value.(subDocMultiValue); isMulti {
		op.Value = multiVal
	} else {
		op.Value, _ = json.Marshal(value)
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds multiple values as elements to an array.
// `values` must be an array type
// ArrayAppendMulti("path", []int{1,2,3,4}, true) =>
//   "path" [..., 1,2,3,4]
//
// This is a more efficient version (at both the network and server levels)
// of doing
// ArrayAppend("path", 1, true).ArrayAppend("path", 2, true).ArrayAppend("path", 3, true)
//
// See ArrayAppend() for more information
func (set *MutateInBuilder) ArrayAppendMulti(path string, values interface{}, createParents bool) *MutateInBuilder {
	return set.ArrayAppend(path, set.marshalArrayMulti(values), createParents)
}

// Adds multiple values at the beginning of an array.
// See ArrayAppendMulti for more information about multiple element operations
// and ArrayPrepend for the semantics of this operation
func (set *MutateInBuilder) ArrayPrependMulti(path string, values interface{}, createParents bool) *MutateInBuilder {
	return set.ArrayPrepend(path, set.marshalArrayMulti(values), createParents)
}

// Inserts multiple elements at a specified position within the
// array. See ArrayAppendMulti for more information about multiple element
// operations, and ArrayInsert for more information about array insertion operations
func (set *MutateInBuilder) ArrayInsertMulti(path string, values interface{}) *MutateInBuilder {
	return set.ArrayInsert(path, set.marshalArrayMulti(values))
}

// Adds an dictionary add unique operation to this mutation operation set.
func (set *MutateInBuilder) ArrayAddUnique(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayAddUnique,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an counter operation to this mutation operation set.
func (set *MutateInBuilder) Counter(path string, delta int64, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpCounter,
		Path: path,
	}
	op.Value, _ = json.Marshal(delta)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

func (b *Bucket) mutateIn(set *MutateInBuilder) (resOut *DocumentFragment, errOut error) {
	signal := make(chan bool, 1)
	op, err := b.client.SubDocMutate([]byte(set.name), set.ops, set.cas, set.expiry,
		func(results []gocbcore.SubDocResult, cas gocbcore.Cas, mt gocbcore.MutationToken, err error) {
			errOut = err
			if errOut == nil {
				resSet := &DocumentFragment{
					cas: Cas(cas),
					mt:  MutationToken{mt, b},
				}
				resSet.contents = make([]subDocResult, len(results))

				for i := range results {
					resSet.contents[i].path = set.ops[i].Path
					resSet.contents[i].err = results[i].Err
					if results[i].Value != nil {
						resSet.contents[i].data = append([]byte(nil), results[i].Value...)
					}
				}

				resOut = resSet
			}
			signal <- true
		})
	if err != nil {
		return nil, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, ErrTimeout
	}
}

// Creates a sub-document mutation operation builder.
func (b *Bucket) MutateIn(key string, cas Cas, expiry uint32) *MutateInBuilder {
	return &MutateInBuilder{
		bucket: b,
		name:   key,
		cas:    gocbcore.Cas(cas),
		expiry: expiry,
	}
}
<commit_msg>Corrected array append<commit_after>package gocb

import (
	"encoding/json"
	"github.com/couchbase/gocb/gocbcore"
	"log"
)

type subDocResult struct {
	path string
	data []byte
	err  error
}

// Represents multiple chunks of a full Document.
type DocumentFragment struct {
	cas      Cas
	mt       MutationToken
	contents []subDocResult
	pathMap  map[string]int
}

// Returns the Cas of the Document
func (frag *DocumentFragment) Cas() Cas {
	return frag.cas
}

// Returns the MutationToken for the change represented by this DocumentFragment.
func (frag *DocumentFragment) MutationToken() MutationToken {
	return frag.mt
}

// Retrieve the value of the operation by its index. The index is the position of
// the operation as it was added to the builder.
func (frag *DocumentFragment) ContentByIndex(idx int, valuePtr interface{}) error {
	res := frag.contents[idx]
	if res.err != nil {
		return res.err
	}
	if valuePtr == nil {
		return nil
	}
	return json.Unmarshal(res.data, valuePtr)
}

// Retrieve the value of the operation by its path. The path is the path provided
// to the operation
func (frag *DocumentFragment) Content(path string, valuePtr interface{}) error {
	if frag.pathMap == nil {
		frag.pathMap = make(map[string]int)
		for i, v := range frag.contents {
			frag.pathMap[v.path] = i
		}
	}
	return frag.ContentByIndex(frag.pathMap[path], valuePtr)
}

// Checks whether the indicated path exists in this DocumentFragment and no
// errors were returned from the server.
func (frag *DocumentFragment) Exists(path string) bool {
	err := frag.Content(path, nil)
	return err == nil
}

// Builder used to create a set of sub-document lookup operations.
type LookupInBuilder struct {
	bucket *Bucket
	name   string
	ops    []gocbcore.SubDocOp
}

// Executes this set of lookup operations on the bucket.
func (set *LookupInBuilder) Execute() (*DocumentFragment, error) {
	return set.bucket.lookupIn(set)
}

// Indicate a path to be retrieved from the document.  The value of the path
// can later be retrieved (after .Execute()) using the Content or ContentByIndex
// method. The path syntax follows N1QL's path syntax (e.g. `foo.bar.baz`).
func (set *LookupInBuilder) Get(path string) *LookupInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpGet,
		Path: path,
	}
	set.ops = append(set.ops, op)
	return set
}

// Similar to Get(), but does not actually retrieve the value from the server.
// This may save bandwidth if you only need to check for the existence of a
// path (without caring for its content). You can check the status of this
// operation by using .Content (and ignoring the value) or .Exists()
func (set *LookupInBuilder) Exists(path string) *LookupInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpExists,
		Path: path,
	}
	set.ops = append(set.ops, op)
	return set
}

func (b *Bucket) lookupIn(set *LookupInBuilder) (resOut *DocumentFragment, errOut error) {
	signal := make(chan bool, 1)
	op, err := b.client.SubDocLookup([]byte(set.name), set.ops,
		func(results []gocbcore.SubDocResult, cas gocbcore.Cas, err error) {
			errOut = err

			{
				resSet := &DocumentFragment{}
				resSet.contents = make([]subDocResult, len(results))

				for i := range results {
					resSet.contents[i].path = set.ops[i].Path
					resSet.contents[i].err = results[i].Err
					if results[i].Value != nil {
						resSet.contents[i].data = append([]byte(nil), results[i].Value...)
					}
				}

				resOut = resSet
			}

			signal <- true
		})
	if err != nil {
		return nil, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, ErrTimeout
	}
}

// Creates a sub-document lookup operation builder.
func (b *Bucket) LookupIn(key string) *LookupInBuilder {
	return &LookupInBuilder{
		bucket: b,
		name:   key,
	}
}

// Builder used to create a set of sub-document mutation operations.
type MutateInBuilder struct {
	bucket *Bucket
	name   string
	cas    gocbcore.Cas
	expiry uint32
	ops    []gocbcore.SubDocOp
}

// Executes this set of mutation operations on the bucket.
func (set *MutateInBuilder) Execute() (*DocumentFragment, error) {
	return set.bucket.mutateIn(set)
}

// Adds an insert operation to this mutation operation set.
func (set *MutateInBuilder) Insert(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpDictAdd,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an upsert operation to this mutation operation set.
func (set *MutateInBuilder) Upsert(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpDictSet,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an replace operation to this mutation operation set.
func (set *MutateInBuilder) Replace(path string, value interface{}) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpReplace,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	set.ops = append(set.ops, op)
	return set
}

func (set *MutateInBuilder) marshalArrayMulti(in interface{}) (out []byte) {
	out, err := json.Marshal(in)
	if err != nil {
		log.Panic(err)
	}

	// Assert first character is a '['
	if len(out) < 2 || out[0] != '[' {
		log.Panic("Not a JSON array")
	}

	out = out[1 : len(out)-1]
	return
}

// Adds an remove operation to this mutation operation set.
func (set *MutateInBuilder) Remove(path string) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpDelete,
		Path: path,
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an element to the beginning (i.e. left) of an array
func (set *MutateInBuilder) ArrayPrepend(path string, value interface{}, createParents bool) *MutateInBuilder {
	jsonVal, _ := json.Marshal(value)

	return set.arrayPrependValue(path, jsonVal, createParents)
}

func (set *MutateInBuilder) arrayPrependValue(path string, bytes []byte, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayPushFirst,
		Path: path,
	}

	op.Value = bytes

	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an element to the end (i.e. right) of an array
func (set *MutateInBuilder) ArrayAppend(path string, value interface{}, createParents bool) *MutateInBuilder {
	jsonVal, _ := json.Marshal(value)

	return set.arrayAppendValue(path, jsonVal, createParents)
}

func (set *MutateInBuilder) arrayAppendValue(path string, bytes []byte, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayPushLast,
		Path: path,
	}

	op.Value = bytes

	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Inserts an element at a given position within an array. The position should be
// specified as part of the path, e.g. path.to.array[3]
func (set *MutateInBuilder) ArrayInsert(path string, value interface{}) *MutateInBuilder {
	jsonVal, _ := json.Marshal(value)

	return set.arrayInsertValue(path, jsonVal)
}

func (set *MutateInBuilder) arrayInsertValue(path string, bytes []byte) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayInsert,
		Path: path,
	}

	op.Value = bytes
	set.ops = append(set.ops, op)
	return set
}

// Adds multiple values as elements to an array.
// `values` must be an array type
// ArrayAppendMulti("path", []int{1,2,3,4}, true) =>
//   "path" [..., 1,2,3,4]
//
// This is a more efficient version (at both the network and server levels)
// of doing
// ArrayAppend("path", 1, true).ArrayAppend("path", 2, true).ArrayAppend("path", 3, true)
//
// See ArrayAppend() for more information
func (set *MutateInBuilder) ArrayAppendMulti(path string, values interface{}, createParents bool) *MutateInBuilder {
	return set.arrayAppendValue(path, set.marshalArrayMulti(values), createParents)
}

// Adds multiple values at the beginning of an array.
// See ArrayAppendMulti for more information about multiple element operations
// and ArrayPrepend for the semantics of this operation
func (set *MutateInBuilder) ArrayPrependMulti(path string, values interface{}, createParents bool) *MutateInBuilder {
	return set.arrayPrependValue(path, set.marshalArrayMulti(values), createParents)
}

// Inserts multiple elements at a specified position within the
// array. See ArrayAppendMulti for more information about multiple element
// operations, and ArrayInsert for more information about array insertion operations
func (set *MutateInBuilder) ArrayInsertMulti(path string, values interface{}) *MutateInBuilder {
	return set.arrayInsertValue(path, set.marshalArrayMulti(values))
}

// Adds an dictionary add unique operation to this mutation operation set.
func (set *MutateInBuilder) ArrayAddUnique(path string, value interface{}, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpArrayAddUnique,
		Path: path,
	}
	op.Value, _ = json.Marshal(value)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

// Adds an counter operation to this mutation operation set.
func (set *MutateInBuilder) Counter(path string, delta int64, createParents bool) *MutateInBuilder {
	op := gocbcore.SubDocOp{
		Op:   gocbcore.SubDocOpCounter,
		Path: path,
	}
	op.Value, _ = json.Marshal(delta)
	if createParents {
		op.Flags |= gocbcore.SubDocFlagMkDirP
	}
	set.ops = append(set.ops, op)
	return set
}

func (b *Bucket) mutateIn(set *MutateInBuilder) (resOut *DocumentFragment, errOut error) {
	signal := make(chan bool, 1)
	op, err := b.client.SubDocMutate([]byte(set.name), set.ops, set.cas, set.expiry,
		func(results []gocbcore.SubDocResult, cas gocbcore.Cas, mt gocbcore.MutationToken, err error) {
			errOut = err
			if errOut == nil {
				resSet := &DocumentFragment{
					cas: Cas(cas),
					mt:  MutationToken{mt, b},
				}
				resSet.contents = make([]subDocResult, len(results))

				for i := range results {
					resSet.contents[i].path = set.ops[i].Path
					resSet.contents[i].err = results[i].Err
					if results[i].Value != nil {
						resSet.contents[i].data = append([]byte(nil), results[i].Value...)
					}
				}

				resOut = resSet
			}
			signal <- true
		})
	if err != nil {
		return nil, err
	}

	timeoutTmr := gocbcore.AcquireTimer(b.opTimeout)
	select {
	case <-signal:
		gocbcore.ReleaseTimer(timeoutTmr, false)
		return
	case <-timeoutTmr.C:
		gocbcore.ReleaseTimer(timeoutTmr, true)
		if !op.Cancel() {
			<-signal
			return
		}
		return nil, ErrTimeout
	}
}

// Creates a sub-document mutation operation builder.
func (b *Bucket) MutateIn(key string, cas Cas, expiry uint32) *MutateInBuilder {
	return &MutateInBuilder{
		bucket: b,
		name:   key,
		cas:    gocbcore.Cas(cas),
		expiry: expiry,
	}
}
<|endoftext|>


<commit_before>package channeldb

import (
	"bytes"
	"net"
	"testing"

	"github.com/lightningnetwork/lnd/tor"
)

// TestAddrSerialization tests that the serialization method used by channeldb
// for net.Addr's works as intended.
func TestAddrSerialization(t *testing.T) {
	t.Parallel()

	testAddrs := []net.Addr{
		&net.TCPAddr{
			IP:   net.ParseIP("192.168.1.1"),
			Port: 12345,
		},
		&net.TCPAddr{
			IP:   net.ParseIP("2001:0db8:0000:0000:0000:ff00:0042:8329"),
			Port: 65535,
		},
		&tor.OnionAddr{
			OnionService: "3g2upl4pq6kufc4m.onion",
			Port:         9735,
		},
		&tor.OnionAddr{
			OnionService: "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion",
			Port:         80,
		},
	}

	var b bytes.Buffer
	for _, expectedAddr := range testAddrs {
		if err := serializeAddr(&b, expectedAddr); err != nil {
			t.Fatalf("unable to serialize address: %v", err)
		}

		addr, err := deserializeAddr(&b)
		if err != nil {
			t.Fatalf("unable to deserialize address: %v", err)
		}

		if addr.String() != expectedAddr.String() {
			t.Fatalf("expected address %v after serialization, "+
				"got %v", addr, expectedAddr)
		}
	}
}
<commit_msg>channledb/addr_test: add negative test for unknown types<commit_after>package channeldb

import (
	"bytes"
	"net"
	"testing"

	"github.com/lightningnetwork/lnd/tor"
)

type unknownAddrType struct{}

func (_ unknownAddrType) Network() string { return "unknown" }
func (_ unknownAddrType) String() string  { return "unknown" }

var addrTests = []struct {
	expAddr net.Addr
	serErr  error
}{
	{
		expAddr: &net.TCPAddr{
			IP:   net.ParseIP("192.168.1.1"),
			Port: 12345,
		},
	},
	{
		expAddr: &net.TCPAddr{
			IP:   net.ParseIP("2001:0db8:0000:0000:0000:ff00:0042:8329"),
			Port: 65535,
		},
	},
	{
		expAddr: &tor.OnionAddr{
			OnionService: "3g2upl4pq6kufc4m.onion",
			Port:         9735,
		},
	},
	{
		expAddr: &tor.OnionAddr{
			OnionService: "vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion",
			Port:         80,
		},
	},
	{
		expAddr: unknownAddrType{},
		serErr:  ErrUnknownAddressType,
	},
}

// TestAddrSerialization tests that the serialization method used by channeldb
// for net.Addr's works as intended.
func TestAddrSerialization(t *testing.T) {
	t.Parallel()

	var b bytes.Buffer
	for _, test := range addrTests {
		err := serializeAddr(&b, test.expAddr)
		if err != test.serErr {
			t.Fatalf("unexpected serialization err for addr %v, "+
				"want: %v, got %v",
				test.expAddr, test.serErr, err)
		} else if test.serErr != nil {
			continue
		}

		addr, err := deserializeAddr(&b)
		if err != nil {
			t.Fatalf("unable to deserialize address: %v", err)
		}

		if addr.String() != test.expAddr.String() {
			t.Fatalf("expected address %v after serialization, "+
				"got %v", addr, test.expAddr)
		}
	}
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

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

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

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

package perm

import (
	"sync"

	"github.com/admpub/nging/application/registry/navigate"
	"github.com/webx-top/echo"
)

var (
	navTreeCached *Map
	navTreeOnce   sync.Once
)

func initNavTreeCached() {
	if navTreeCached != nil {
		return
	}
	navTreeCached = NewMap()
	for _, project := range navigate.ProjectListAll() {
		if project == nil {
			continue
		}
		navTreeCached.Import(project.NavList)
	}
	navTreeCached.Import(navigate.TopNavigate)
}

func NavTreeCached() *Map {
	navTreeOnce.Do(initNavTreeCached)
	return navTreeCached
}

func init() {
	echo.On(`beforeRun`, func(_ echo.H) error {
		NavTreeCached()
		return nil
	})
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

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

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

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

package perm

import (
	"sync"

	"github.com/admpub/nging/application/registry/navigate"
	"github.com/webx-top/echo"
)

var (
	navTreeCached *Map
	navTreeOnce   sync.Once
)

func initNavTreeCached() {
	navTreeCached = NewMap()
	for _, project := range navigate.ProjectListAll() {
		if project == nil {
			continue
		}
		navTreeCached.Import(project.NavList)
	}
	navTreeCached.Import(navigate.TopNavigate)
}

func NavTreeCached() *Map {
	navTreeOnce.Do(initNavTreeCached)
	return navTreeCached
}

func init() {
	echo.On(`beforeRun`, func(_ echo.H) error {
		NavTreeCached()
		return nil
	})
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/types"
)

// storageRenter unlocks the wallet, mines some currency, sets an allowance
// using that currency, and uploads some files.  It will periodically try to
// download or delete those files, printing any errors that occur.
func (j *JobRunner) storageRenter() {
	j.tg.Add()
	defer j.tg.Done()

	err := j.client.Post("/wallet/unlock", fmt.Sprintf("encryptionpassword=%s&dictionary=%s", j.walletPassword, "english"), nil)
	if err != nil {
		log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	err = j.client.Get("/miner/start", nil)
	if err != nil {
		log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	// Mine at least 100,000 SC
	desiredbalance := types.NewCurrency64(100000).Mul(types.SiacoinPrecision)
	success := false
	for start := time.Now(); time.Since(start) < 5*time.Minute; {
		select {
		case <-j.tg.StopChan():
			return
		case <-time.After(time.Second):
		}

		var walletInfo api.WalletGET
		err = j.client.Get("/wallet", &walletInfo)
		if err != nil {
			log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
			return
		}
		if walletInfo.ConfirmedSiacoinBalance.Cmp(desiredbalance) > 0 {
			success = true
			break
		}
	}
	if !success {
		log.Printf("[%v jobStorageRenter ERROR]: timeout: could not mine enough currency after 5 minutes\n", j.siaDirectory)
		return
	}

	// Set an allowance using a 100 block period and 50ksc.
	// TODO: verify that spending does not exceed the set allowance.
	allowance := types.NewCurrency64(50000).Mul(types.SiacoinPrecision)
	if err := j.client.Post("/renter", fmt.Sprintf("funds=%v&period=100", allowance), nil); err != nil {
		log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
		return
	}
	log.Printf("[%v jobStorageRenter INFO]: successfully set allowance\n", j.siaDirectory)

	// Every 120 seconds, upload a 500MB file.  After ten files, delete one file
	// at random each iteration.
	go func() {
		var files []string

		// Clean up by deleting any created files when this goroutine returns.
		defer func() {
			for _, file := range files {
				os.Remove(file)
			}
		}()

		for i := 0; ; i++ {
			select {
			case <-j.tg.StopChan():
				return
			case <-time.After(time.Second * 120):
			}
			func() {
				j.tg.Add()
				defer j.tg.Done()

				if i >= 10 {
					randindex, err := crypto.RandIntn(len(files))
					if err != nil {
						log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
						return
					}
					if err = j.client.Post(fmt.Sprintf("/renter/delete/%v", files[randindex]), "", nil); err != nil {
						log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
						return
					}
					log.Printf("[%v jobStorageRenter INFO]: successfully deleted file\n", j.siaDirectory)
					os.Remove(files[randindex])
					files = append(files[:randindex], files[randindex+1:]...)
				}

				// Generate some random data to upload
				f, err := ioutil.TempFile("", "antfarm-renter")
				if err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}
				files = append(files, f.Name())

				_, err = io.CopyN(f, rand.Reader, 500e6)
				if err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}

				// Upload the random data
				if err = j.client.Post(fmt.Sprintf("/renter/upload/%v", f.Name()), fmt.Sprintf("source=%v", f.Name()), nil); err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}
				log.Printf("[%v jobStorageRenter INFO]: succesfully uploaded file\n", j.siaDirectory)
			}()
		}
	}()

	// Every 200 seconds, attempt to download a file.  Verify that the download call
	// succeeds correctly, the file is placed in the download list, and the file
	// is removed from the download list, indicating successful download
	// completion.
	go func() {
		for {
			select {
			case <-j.tg.StopChan():
				return
			case <-time.After(time.Second * 200):
			}

			func() {
				j.tg.Add()
				defer j.tg.Done()

				// Download a random file from the renter's file list
				var renterFiles api.RenterFiles
				if err := j.client.Get("/renter/files", &renterFiles); err != nil {
					log.Printf("%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
				}

				// Do nothing if there are not any files to be downloaded.
				if len(renterFiles.Files) == 0 {
					return
				}

				// Filter out files which are not available.
				availableFiles := renterFiles.Files[:0]
				for _, file := range renterFiles.Files {
					if file.Available {
						availableFiles = append(availableFiles, file)
					}
				}

				// Download a file at random.
				randindex, _ := crypto.RandIntn(len(availableFiles))
				fileToDownload := availableFiles[randindex]

				f, err := ioutil.TempFile("", "antfarm-renter")
				if err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
				}
				defer os.Remove(f.Name())

				if err = j.client.Post(fmt.Sprintf("/renter/download/%v", fileToDownload.SiaPath), fmt.Sprintf("destination=%v", f.Name()), nil); err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}

				// isFileInDownloads grabs the files currently being downloaded by the
				// renter and returns bool `true` if fileToDownload exists in the
				// download list.
				isFileInDownloads := func() bool {
					var renterDownloads api.RenterDownloadQueue
					if err = j.client.Get("/renter/downloads", &renterDownloads); err != nil {
						log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					}

					hasFile := false
					for _, download := range renterDownloads.Downloads {
						if download.SiaPath == fileToDownload.SiaPath {
							hasFile = true
						}
					}

					return hasFile
				}

				// Wait for the file to appear in the download list
				success := false
				for start := time.Now(); time.Since(start) < 1*time.Minute; {
					select {
					case <-j.tg.StopChan():
						break
					case <-time.After(time.Second):
					}

					if isFileInDownloads() {
						success = true
						break
					}
				}
				if !success {
					log.Printf("[%v jobStorageRenter ERROR]: file %v did not appear in the renter download list\n", j.siaDirectory, fileToDownload.SiaPath)
					return
				}

				// Wait for the file to be finished downloading, with a timeout of 15 minutes.
				success = false
				for start := time.Now(); time.Since(start) < 15*time.Minute; {
					select {
					case <-j.tg.StopChan():
						break
					case <-time.After(time.Second):
					}

					if !isFileInDownloads() {
						success = true
						break
					}
				}
				if !success {
					log.Printf("[%v jobStorageRenter ERROR]: file %v did not complete downloading\n", j.siaDirectory, fileToDownload.SiaPath)
					return
				}
				log.Printf("[%v jobStorageRenter INFO]: succesfully downloaded file\n", j.siaDirectory)
			}()
		}
	}()
}
<commit_msg>retry renter setallowance up to 5 times<commit_after>package main

import (
	"crypto/rand"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"time"

	"github.com/NebulousLabs/Sia/api"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/types"
)

// storageRenter unlocks the wallet, mines some currency, sets an allowance
// using that currency, and uploads some files.  It will periodically try to
// download or delete those files, printing any errors that occur.
func (j *JobRunner) storageRenter() {
	j.tg.Add()
	defer j.tg.Done()

	err := j.client.Post("/wallet/unlock", fmt.Sprintf("encryptionpassword=%s&dictionary=%s", j.walletPassword, "english"), nil)
	if err != nil {
		log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	err = j.client.Get("/miner/start", nil)
	if err != nil {
		log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
		return
	}

	// Mine at least 100,000 SC
	desiredbalance := types.NewCurrency64(100000).Mul(types.SiacoinPrecision)
	success := false
	for start := time.Now(); time.Since(start) < 5*time.Minute; {
		select {
		case <-j.tg.StopChan():
			return
		case <-time.After(time.Second):
		}

		var walletInfo api.WalletGET
		err = j.client.Get("/wallet", &walletInfo)
		if err != nil {
			log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
			return
		}
		if walletInfo.ConfirmedSiacoinBalance.Cmp(desiredbalance) > 0 {
			success = true
			break
		}
	}
	if !success {
		log.Printf("[%v jobStorageRenter ERROR]: timeout: could not mine enough currency after 5 minutes\n", j.siaDirectory)
		return
	}
	// Set an allowance using a 100 block period and 50ksc.  Retry up to 5 times on error.
	// TODO: verify that spending does not exceed the set allowance.
	success = false
	for try := 0; try < 5; try++ {
		allowance := types.NewCurrency64(50000).Mul(types.SiacoinPrecision)
		if err := j.client.Post("/renter", fmt.Sprintf("funds=%v&period=100", allowance), nil); err != nil {
			log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
		} else {
			success = true
			break
		}
		time.Sleep(time.Second * 5)
	}
	if !success {
		log.Printf("[%v jobStorageRenter ERROR]: could not set allowance after 5 tries\n", j.siaDirectory)
		return
	}
	log.Printf("[%v jobStorageRenter INFO]: successfully set allowance\n", j.siaDirectory)

	// Every 120 seconds, upload a 500MB file.  After ten files, delete one file
	// at random each iteration.
	go func() {
		var files []string

		// Clean up by deleting any created files when this goroutine returns.
		defer func() {
			for _, file := range files {
				os.Remove(file)
			}
		}()

		for i := 0; ; i++ {
			select {
			case <-j.tg.StopChan():
				return
			case <-time.After(time.Second * 120):
			}
			func() {
				j.tg.Add()
				defer j.tg.Done()

				if i >= 10 {
					randindex, err := crypto.RandIntn(len(files))
					if err != nil {
						log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
						return
					}
					if err = j.client.Post(fmt.Sprintf("/renter/delete/%v", files[randindex]), "", nil); err != nil {
						log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
						return
					}
					log.Printf("[%v jobStorageRenter INFO]: successfully deleted file\n", j.siaDirectory)
					os.Remove(files[randindex])
					files = append(files[:randindex], files[randindex+1:]...)
				}

				// Generate some random data to upload
				f, err := ioutil.TempFile("", "antfarm-renter")
				if err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}
				files = append(files, f.Name())

				_, err = io.CopyN(f, rand.Reader, 500e6)
				if err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}

				// Upload the random data
				if err = j.client.Post(fmt.Sprintf("/renter/upload/%v", f.Name()), fmt.Sprintf("source=%v", f.Name()), nil); err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}
				log.Printf("[%v jobStorageRenter INFO]: succesfully uploaded file\n", j.siaDirectory)
			}()
		}
	}()

	// Every 200 seconds, attempt to download a file.  Verify that the download call
	// succeeds correctly, the file is placed in the download list, and the file
	// is removed from the download list, indicating successful download
	// completion.
	go func() {
		for {
			select {
			case <-j.tg.StopChan():
				return
			case <-time.After(time.Second * 200):
			}

			func() {
				j.tg.Add()
				defer j.tg.Done()

				// Download a random file from the renter's file list
				var renterFiles api.RenterFiles
				if err := j.client.Get("/renter/files", &renterFiles); err != nil {
					log.Printf("%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
				}

				// Do nothing if there are not any files to be downloaded.
				if len(renterFiles.Files) == 0 {
					return
				}

				// Filter out files which are not available.
				availableFiles := renterFiles.Files[:0]
				for _, file := range renterFiles.Files {
					if file.Available {
						availableFiles = append(availableFiles, file)
					}
				}

				// Download a file at random.
				randindex, _ := crypto.RandIntn(len(availableFiles))
				fileToDownload := availableFiles[randindex]

				f, err := ioutil.TempFile("", "antfarm-renter")
				if err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
				}
				defer os.Remove(f.Name())

				if err = j.client.Post(fmt.Sprintf("/renter/download/%v", fileToDownload.SiaPath), fmt.Sprintf("destination=%v", f.Name()), nil); err != nil {
					log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					return
				}

				// isFileInDownloads grabs the files currently being downloaded by the
				// renter and returns bool `true` if fileToDownload exists in the
				// download list.
				isFileInDownloads := func() bool {
					var renterDownloads api.RenterDownloadQueue
					if err = j.client.Get("/renter/downloads", &renterDownloads); err != nil {
						log.Printf("[%v jobStorageRenter ERROR]: %v\n", j.siaDirectory, err)
					}

					hasFile := false
					for _, download := range renterDownloads.Downloads {
						if download.SiaPath == fileToDownload.SiaPath {
							hasFile = true
						}
					}

					return hasFile
				}

				// Wait for the file to appear in the download list
				success := false
				for start := time.Now(); time.Since(start) < 1*time.Minute; {
					select {
					case <-j.tg.StopChan():
						break
					case <-time.After(time.Second):
					}

					if isFileInDownloads() {
						success = true
						break
					}
				}
				if !success {
					log.Printf("[%v jobStorageRenter ERROR]: file %v did not appear in the renter download list\n", j.siaDirectory, fileToDownload.SiaPath)
					return
				}

				// Wait for the file to be finished downloading, with a timeout of 15 minutes.
				success = false
				for start := time.Now(); time.Since(start) < 15*time.Minute; {
					select {
					case <-j.tg.StopChan():
						break
					case <-time.After(time.Second):
					}

					if !isFileInDownloads() {
						success = true
						break
					}
				}
				if !success {
					log.Printf("[%v jobStorageRenter ERROR]: file %v did not complete downloading\n", j.siaDirectory, fileToDownload.SiaPath)
					return
				}
				log.Printf("[%v jobStorageRenter INFO]: succesfully downloaded file\n", j.siaDirectory)
			}()
		}
	}()
}
<|endoftext|>


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

package gcsproxy_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"testing"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

func TestOgletest(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// An oglemock.Matcher that accepts a predicate function and a description,
// making it easy to make anonymous matcher types.
type predicateMatcher struct {
	Desc      string
	Predicate func(interface{}) error
}

var _ Matcher = &predicateMatcher{}

func (m *predicateMatcher) Matches(candidate interface{}) error {
	return m.Predicate(candidate)
}

func (m *predicateMatcher) Description() string {
	return m.Desc
}

func nameIs(name string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Name is: %s", name),
		Predicate: func(candidate interface{}) error {
			req := candidate.(*gcs.CreateObjectRequest)
			if req.Attrs.Name != name {
				return errors.New("")
			}

			return nil
		},
	}
}

func contentsAre(s string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Object contents are: %s", s),
		Predicate: func(candidate interface{}) error {
			// Snarf the contents.
			req := candidate.(*gcs.CreateObjectRequest)
			contents, err := ioutil.ReadAll(req.Contents)
			if err != nil {
				panic(err)
			}

			// Compare
			if string(contents) != s {
				return errors.New("")
			}

			return nil
		},
	}
}

////////////////////////////////////////////////////////////////////////
// Invariant-checking object proxy
////////////////////////////////////////////////////////////////////////

// A wrapper around ObjectProxy that calls CheckInvariants whenever invariants
// should hold. For catching logic errors early in the test.
type checkingObjectProxy struct {
	wrapped *gcsproxy.ObjectProxy
}

func (op *checkingObjectProxy) Name() string {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Name()
}

func (op *checkingObjectProxy) Stat() (uint64, bool, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Stat(context.Background())
}

func (op *checkingObjectProxy) ReadAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.ReadAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) WriteAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.WriteAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) Truncate(n uint64) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Truncate(context.Background(), n)
}

func (op *checkingObjectProxy) Sync() (uint64, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Sync(context.Background())
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ObjectProxyTest struct {
	objectName string
	bucket     mock_gcs.MockBucket
	op         checkingObjectProxy
}

func (t *ObjectProxyTest) setUp(ti *TestInfo, srcGeneration uint64) {
	t.objectName = "some/object"
	t.bucket = mock_gcs.NewMockBucket(ti.MockController, "bucket")

	var err error
	t.op.wrapped, err = gcsproxy.NewObjectProxy(
		context.Background(),
		t.bucket,
		t.objectName,
		srcGeneration)

	if err != nil {
		panic(err)
	}
}

////////////////////////////////////////////////////////////////////////
// No source object
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are a fresh object proxy without a source
// object set.
type NoSourceObjectTest struct {
	ObjectProxyTest
}

var _ SetUpInterface = &NoSourceObjectTest{}

func init() { RegisterTestSuite(&NoSourceObjectTest{}) }

func (t *NoSourceObjectTest) SetUp(ti *TestInfo) {
	t.ObjectProxyTest.setUp(ti, 0)
}

func (t *NoSourceObjectTest) Name() {
	ExpectEq(t.objectName, t.op.Name())
}

func (t *NoSourceObjectTest) Read_InitialState() {
	buf := make([]byte, 1024)
	n, err := t.op.ReadAt(buf, 0)

	ExpectEq(io.EOF, err)
	ExpectEq(0, n)
}

func (t *NoSourceObjectTest) WriteToEndOfObjectThenRead() {
	var buf []byte
	var n int
	var err error

	// Extend the object by writing twice.
	n, err = t.op.WriteAt([]byte("taco"), 0)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	n, err = t.op.WriteAt([]byte("burrito"), int64(len("taco")))
	AssertEq(nil, err)
	AssertEq(len("burrito"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("tacoburrito"), n)
	ExpectEq("tacoburrito", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 3)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("obur", string(buf[:n]))
}

func (t *NoSourceObjectTest) WritePastEndOfObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Extend the object by writing past its end.
	n, err = t.op.WriteAt([]byte("taco"), 2)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(2+len("taco"), n)
	ExpectEq("\x00\x00taco", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 1)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("\x00tac", string(buf[:n]))
}

func (t *NoSourceObjectTest) WriteWithinObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Write several bytes to extend the object.
	n, err = t.op.WriteAt([]byte("00000"), 0)
	AssertEq(nil, err)
	AssertEq(len("00000"), n)

	// Overwrite some in the middle.
	n, err = t.op.WriteAt([]byte("11"), 1)
	AssertEq(nil, err)
	AssertEq(len("11"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("01100"), n)
	ExpectEq("01100", string(buf[:n]))
}

func (t *NoSourceObjectTest) GrowByTruncating() {
	var n int
	var err error
	var buf []byte

	// Truncate
	err = t.op.Truncate(4)
	AssertEq(nil, err)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(4, n)
	ExpectEq("\x00\x00\x00\x00", string(buf[:n]))
}

func (t *NoSourceObjectTest) Sync_NoInteractions() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_ReadCallsOnly() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_AfterTruncating() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_CreateObjectFails() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_CreateObjectSaysPreconditionFailed() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_Successful() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_CallsBucket() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_BucketFails() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_InitialState() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterShortening() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterGrowing() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterReading() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_Clobbered() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// Source object present
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are an object proxy branching from a source
// object in the bucket.
type SourceObjectPresentTest struct {
	ObjectProxyTest
	sourceObject *storage.Object
}

var _ SetUpInterface = &SourceObjectPresentTest{}

func init() { RegisterTestSuite(&SourceObjectPresentTest{}) }

func (t *SourceObjectPresentTest) SetUp(ti *TestInfo) {
	t.ObjectProxyTest.setUp(ti, 123)
}

func (t *SourceObjectPresentTest) Read_CallsNewReader() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Read_NewReaderFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Read_NewReaderSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Write_CallsNewReader() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Write_NewReaderFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Write_NewReaderSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Truncate_CallsNewReader() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Truncate_NewReaderFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Truncate_NewReaderSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_NoInteractions() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_CallsCreateObject() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_CallsBucket() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_BucketFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_BucketSaysNotFound() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_InitialState() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterShortening() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterGrowing() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterReading() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_Clobbered() {
	AssertTrue(false, "TODO")
}
<commit_msg>Fixed Sync test names.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcsproxy_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"testing"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/mock_gcs"
	"github.com/jacobsa/gcsfuse/gcsproxy"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

func TestOgletest(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// An oglemock.Matcher that accepts a predicate function and a description,
// making it easy to make anonymous matcher types.
type predicateMatcher struct {
	Desc      string
	Predicate func(interface{}) error
}

var _ Matcher = &predicateMatcher{}

func (m *predicateMatcher) Matches(candidate interface{}) error {
	return m.Predicate(candidate)
}

func (m *predicateMatcher) Description() string {
	return m.Desc
}

func nameIs(name string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Name is: %s", name),
		Predicate: func(candidate interface{}) error {
			req := candidate.(*gcs.CreateObjectRequest)
			if req.Attrs.Name != name {
				return errors.New("")
			}

			return nil
		},
	}
}

func contentsAre(s string) Matcher {
	return &predicateMatcher{
		Desc: fmt.Sprintf("Object contents are: %s", s),
		Predicate: func(candidate interface{}) error {
			// Snarf the contents.
			req := candidate.(*gcs.CreateObjectRequest)
			contents, err := ioutil.ReadAll(req.Contents)
			if err != nil {
				panic(err)
			}

			// Compare
			if string(contents) != s {
				return errors.New("")
			}

			return nil
		},
	}
}

////////////////////////////////////////////////////////////////////////
// Invariant-checking object proxy
////////////////////////////////////////////////////////////////////////

// A wrapper around ObjectProxy that calls CheckInvariants whenever invariants
// should hold. For catching logic errors early in the test.
type checkingObjectProxy struct {
	wrapped *gcsproxy.ObjectProxy
}

func (op *checkingObjectProxy) Name() string {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Name()
}

func (op *checkingObjectProxy) Stat() (uint64, bool, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Stat(context.Background())
}

func (op *checkingObjectProxy) ReadAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.ReadAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) WriteAt(b []byte, o int64) (int, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.WriteAt(context.Background(), b, o)
}

func (op *checkingObjectProxy) Truncate(n uint64) error {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Truncate(context.Background(), n)
}

func (op *checkingObjectProxy) Sync() (uint64, error) {
	op.wrapped.CheckInvariants()
	defer op.wrapped.CheckInvariants()
	return op.wrapped.Sync(context.Background())
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type ObjectProxyTest struct {
	objectName string
	bucket     mock_gcs.MockBucket
	op         checkingObjectProxy
}

func (t *ObjectProxyTest) setUp(ti *TestInfo, srcGeneration uint64) {
	t.objectName = "some/object"
	t.bucket = mock_gcs.NewMockBucket(ti.MockController, "bucket")

	var err error
	t.op.wrapped, err = gcsproxy.NewObjectProxy(
		context.Background(),
		t.bucket,
		t.objectName,
		srcGeneration)

	if err != nil {
		panic(err)
	}
}

////////////////////////////////////////////////////////////////////////
// No source object
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are a fresh object proxy without a source
// object set.
type NoSourceObjectTest struct {
	ObjectProxyTest
}

var _ SetUpInterface = &NoSourceObjectTest{}

func init() { RegisterTestSuite(&NoSourceObjectTest{}) }

func (t *NoSourceObjectTest) SetUp(ti *TestInfo) {
	t.ObjectProxyTest.setUp(ti, 0)
}

func (t *NoSourceObjectTest) Name() {
	ExpectEq(t.objectName, t.op.Name())
}

func (t *NoSourceObjectTest) Read_InitialState() {
	buf := make([]byte, 1024)
	n, err := t.op.ReadAt(buf, 0)

	ExpectEq(io.EOF, err)
	ExpectEq(0, n)
}

func (t *NoSourceObjectTest) WriteToEndOfObjectThenRead() {
	var buf []byte
	var n int
	var err error

	// Extend the object by writing twice.
	n, err = t.op.WriteAt([]byte("taco"), 0)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	n, err = t.op.WriteAt([]byte("burrito"), int64(len("taco")))
	AssertEq(nil, err)
	AssertEq(len("burrito"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("tacoburrito"), n)
	ExpectEq("tacoburrito", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 3)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("obur", string(buf[:n]))
}

func (t *NoSourceObjectTest) WritePastEndOfObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Extend the object by writing past its end.
	n, err = t.op.WriteAt([]byte("taco"), 2)
	AssertEq(nil, err)
	AssertEq(len("taco"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(2+len("taco"), n)
	ExpectEq("\x00\x00taco", string(buf[:n]))

	// Read a range in the middle.
	buf = make([]byte, 4)
	n, err = t.op.ReadAt(buf, 1)

	AssertEq(nil, err)
	ExpectEq(4, n)
	ExpectEq("\x00tac", string(buf[:n]))
}

func (t *NoSourceObjectTest) WriteWithinObjectThenRead() {
	var n int
	var err error
	var buf []byte

	// Write several bytes to extend the object.
	n, err = t.op.WriteAt([]byte("00000"), 0)
	AssertEq(nil, err)
	AssertEq(len("00000"), n)

	// Overwrite some in the middle.
	n, err = t.op.WriteAt([]byte("11"), 1)
	AssertEq(nil, err)
	AssertEq(len("11"), n)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(len("01100"), n)
	ExpectEq("01100", string(buf[:n]))
}

func (t *NoSourceObjectTest) GrowByTruncating() {
	var n int
	var err error
	var buf []byte

	// Truncate
	err = t.op.Truncate(4)
	AssertEq(nil, err)

	// Read the whole thing.
	buf = make([]byte, 1024)
	n, err = t.op.ReadAt(buf, 0)

	AssertEq(io.EOF, err)
	ExpectEq(4, n)
	ExpectEq("\x00\x00\x00\x00", string(buf[:n]))
}

func (t *NoSourceObjectTest) Sync_CallsCreateObject() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_CreateObjectFails() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_CreateObjectSaysPreconditionFailed() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Sync_Successful() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_CallsBucket() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_BucketFails() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_InitialState() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterShortening() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterGrowing() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterReading() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *NoSourceObjectTest) Stat_Clobbered() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// Source object present
////////////////////////////////////////////////////////////////////////

// A test whose initial conditions are an object proxy branching from a source
// object in the bucket.
type SourceObjectPresentTest struct {
	ObjectProxyTest
	sourceObject *storage.Object
}

var _ SetUpInterface = &SourceObjectPresentTest{}

func init() { RegisterTestSuite(&SourceObjectPresentTest{}) }

func (t *SourceObjectPresentTest) SetUp(ti *TestInfo) {
	t.ObjectProxyTest.setUp(ti, 123)
}

func (t *SourceObjectPresentTest) Read_CallsNewReader() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Read_NewReaderFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Read_NewReaderSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Write_CallsNewReader() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Write_NewReaderFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Write_NewReaderSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Truncate_CallsNewReader() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Truncate_NewReaderFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Truncate_NewReaderSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_NoInteractions() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_AfterReading() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_AfterTruncating() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Sync_CallsCreateObject() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_CallsBucket() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_BucketFails() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_BucketSaysNotFound() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_InitialState() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterShortening() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterGrowing() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterReading() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_AfterWriting() {
	AssertTrue(false, "TODO")
}

func (t *SourceObjectPresentTest) Stat_Clobbered() {
	AssertTrue(false, "TODO")
}
<|endoftext|>


<commit_before>package build

import (
	"strconv"
	"strings"
)

const (
	// MaxEncodedVersionLength is the maximum length of a version string encoded
	// with the encode package. 100 is much larger than any version number we send
	// now, but it allows us to send additional information in the version string
	// later if we choose. For example appending the version string with the HEAD
	// commit hash.
	MaxEncodedVersionLength = 100

	// Version is the current version of siad.
	Version = "1.3.1"
)

// IsVersion returns whether str is a valid version number.
func IsVersion(str string) bool {
	for _, n := range strings.Split(str, ".") {
		if _, err := strconv.Atoi(n); err != nil {
			return false
		}
	}
	return true
}

// min returns the smaller of two integers.
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// VersionCmp returns an int indicating the difference between a and b. It
// follows the convention of bytes.Compare and big.Cmp:
//
//   -1 if a <  b
//    0 if a == b
//   +1 if a >  b
//
// One important quirk is that "1.1.0" is considered newer than "1.1", despite
// being numerically equal.
func VersionCmp(a, b string) int {
	aNums := strings.Split(a, ".")
	bNums := strings.Split(b, ".")
	for i := 0; i < min(len(aNums), len(bNums)); i++ {
		// assume that both version strings are valid
		aInt, _ := strconv.Atoi(aNums[i])
		bInt, _ := strconv.Atoi(bNums[i])
		if aInt < bInt {
			return -1
		} else if aInt > bInt {
			return 1
		}
	}
	// all shared digits are equal, but lengths may not be equal
	if len(aNums) < len(bNums) {
		return -1
	} else if len(aNums) > len(bNums) {
		return 1
	}
	// strings are identical
	return 0
}
<commit_msg>pump version to 1.3.2<commit_after>package build

import (
	"strconv"
	"strings"
)

const (
	// MaxEncodedVersionLength is the maximum length of a version string encoded
	// with the encode package. 100 is much larger than any version number we send
	// now, but it allows us to send additional information in the version string
	// later if we choose. For example appending the version string with the HEAD
	// commit hash.
	MaxEncodedVersionLength = 100

	// Version is the current version of siad.
	Version = "1.3.2"
)

// IsVersion returns whether str is a valid version number.
func IsVersion(str string) bool {
	for _, n := range strings.Split(str, ".") {
		if _, err := strconv.Atoi(n); err != nil {
			return false
		}
	}
	return true
}

// min returns the smaller of two integers.
func min(a, b int) int {
	if a < b {
		return a
	}
	return b
}

// VersionCmp returns an int indicating the difference between a and b. It
// follows the convention of bytes.Compare and big.Cmp:
//
//   -1 if a <  b
//    0 if a == b
//   +1 if a >  b
//
// One important quirk is that "1.1.0" is considered newer than "1.1", despite
// being numerically equal.
func VersionCmp(a, b string) int {
	aNums := strings.Split(a, ".")
	bNums := strings.Split(b, ".")
	for i := 0; i < min(len(aNums), len(bNums)); i++ {
		// assume that both version strings are valid
		aInt, _ := strconv.Atoi(aNums[i])
		bInt, _ := strconv.Atoi(bNums[i])
		if aInt < bInt {
			return -1
		} else if aInt > bInt {
			return 1
		}
	}
	// all shared digits are equal, but lengths may not be equal
	if len(aNums) < len(bNums) {
		return -1
	} else if len(aNums) > len(bNums) {
		return 1
	}
	// strings are identical
	return 0
}
<|endoftext|>


<commit_before>package sous

import (
	"fmt"
	"io"

	"github.com/pkg/errors"
	"github.com/samsalisbury/semv"
)

type (
	// SourceLocation identifies a directory inside a source code repository.
	// Note that the directory is ambiguous without the addition of a revision
	// ID. This type is used as a shorthand for deploy manifests, enabling the
	// logical grouping of deploys of different versions of a particular
	// service.
	SourceLocation struct {
		// Repo identifies a source code repository.
		Repo,
		// Dir is a directory within the repository at Repo containing the
		// source code for one piece of software.
		Dir string `yaml:",omitempty"`
	}
)

// NewSourceLocation creates a new SourceLocation from strings.
func NewSourceLocation(repoURL, repoOffset string) SourceLocation {
	return SourceLocation{repoURL, repoOffset}
}

// ParseSourceLocation parses an entire SourceLocation.
func ParseSourceLocation(s string) (SourceLocation, error) {
	chunks := parseChunks(s)
	return sourceLocationFromChunks(s, chunks)
}

// MustParseSourceLocation wraps ParseSourceLocation but panics instead of
// returning a non-nil error.
func MustParseSourceLocation(s string) SourceLocation {
	sl, err := ParseSourceLocation(s)
	if err != nil {
		panic(err)
	}
	return sl
}

func sourceLocationFromChunks(source string, chunks []string) (SourceLocation, error) {
	if len(chunks) > 2 {
		return SourceLocation{}, &IncludesVersion{source}
	}
	if len(chunks[0]) == 0 {
		return SourceLocation{}, &MissingRepo{source}
	}
	repoURL := chunks[0]
	repoOffset := ""
	if len(chunks) > 1 {
		repoOffset = chunks[1]
	}
	return SourceLocation{Repo: repoURL, Dir: repoOffset}, nil
}

// MarshalYAML serializes this SourceLocation to a YAML document.
func (sl SourceLocation) MarshalYAML() (interface{}, error) {
	return sl.String(), nil
}

// MarshalText implements encoding.TextMarshaler.
func (sl SourceLocation) MarshalText() ([]byte, error) {
	return []byte(sl.String()), nil
}

// UnmarshalText implements encoding.TextMarshaler.
func (sl *SourceLocation) UnmarshalText(b []byte) error {
	s := string(b)
	n, err := fmt.Sscanf(s, "%s %s", &sl.Repo, &sl.Dir)
	if err != nil && err != io.EOF {
		return errors.Wrapf(err, "unable to unmarshal source location %q", s)
	}
	if n == 0 {
		return errors.Errorf("incomplete source location %q", s)
	}
	return nil
}

// UnmarshalYAML deserializes a YAML document into this SourceLocation
func (sl *SourceLocation) UnmarshalYAML(unmarshal func(interface{}) error) error {
	s := ""
	if err := unmarshal(&s); err != nil {
		return err
	}
	var err error
	*sl, err = ParseSourceLocation(s)
	return err
}

func (sl SourceLocation) String() string {
	if sl.Dir == "" {
		return fmt.Sprintf("%s", sl.Repo)
	}
	return fmt.Sprintf("%s,%s", sl.Repo, sl.Dir)
}

// SourceID returns a SourceID built from this location with the addition of a version.
func (sl SourceLocation) SourceID(version semv.Version) SourceID {
	return SourceID{
		Location: sl,
		Version:  version,
	}
}
<commit_msg>Remove yaml tag from SourceLocation<commit_after>package sous

import (
	"fmt"
	"io"

	"github.com/pkg/errors"
	"github.com/samsalisbury/semv"
)

type (
	// SourceLocation identifies a directory inside a source code repository.
	// Note that the directory is ambiguous without the addition of a revision
	// ID.
	SourceLocation struct {
		// Repo identifies a source code repository.
		Repo,
		// Dir is a directory within the repository at Repo containing the
		// source code for one piece of software.
		Dir string
	}
)

// NewSourceLocation creates a new SourceLocation from strings.
func NewSourceLocation(repoURL, repoOffset string) SourceLocation {
	return SourceLocation{repoURL, repoOffset}
}

// ParseSourceLocation parses an entire SourceLocation.
func ParseSourceLocation(s string) (SourceLocation, error) {
	chunks := parseChunks(s)
	return sourceLocationFromChunks(s, chunks)
}

// MustParseSourceLocation wraps ParseSourceLocation but panics instead of
// returning a non-nil error.
func MustParseSourceLocation(s string) SourceLocation {
	sl, err := ParseSourceLocation(s)
	if err != nil {
		panic(err)
	}
	return sl
}

func sourceLocationFromChunks(source string, chunks []string) (SourceLocation, error) {
	if len(chunks) > 2 {
		return SourceLocation{}, &IncludesVersion{source}
	}
	if len(chunks[0]) == 0 {
		return SourceLocation{}, &MissingRepo{source}
	}
	repoURL := chunks[0]
	repoOffset := ""
	if len(chunks) > 1 {
		repoOffset = chunks[1]
	}
	return SourceLocation{Repo: repoURL, Dir: repoOffset}, nil
}

// MarshalYAML serializes this SourceLocation to a YAML document.
func (sl SourceLocation) MarshalYAML() (interface{}, error) {
	return sl.String(), nil
}

// MarshalText implements encoding.TextMarshaler.
func (sl SourceLocation) MarshalText() ([]byte, error) {
	return []byte(sl.String()), nil
}

// UnmarshalText implements encoding.TextMarshaler.
func (sl *SourceLocation) UnmarshalText(b []byte) error {
	s := string(b)
	n, err := fmt.Sscanf(s, "%s %s", &sl.Repo, &sl.Dir)
	if err != nil && err != io.EOF {
		return errors.Wrapf(err, "unable to unmarshal source location %q", s)
	}
	if n == 0 {
		return errors.Errorf("incomplete source location %q", s)
	}
	return nil
}

// UnmarshalYAML deserializes a YAML document into this SourceLocation
func (sl *SourceLocation) UnmarshalYAML(unmarshal func(interface{}) error) error {
	s := ""
	if err := unmarshal(&s); err != nil {
		return err
	}
	var err error
	*sl, err = ParseSourceLocation(s)
	return err
}

func (sl SourceLocation) String() string {
	if sl.Dir == "" {
		return fmt.Sprintf("%s", sl.Repo)
	}
	return fmt.Sprintf("%s,%s", sl.Repo, sl.Dir)
}

// SourceID returns a SourceID built from this location with the addition of a version.
func (sl SourceLocation) SourceID(version semv.Version) SourceID {
	return SourceID{
		Location: sl,
		Version:  version,
	}
}
<|endoftext|>


<commit_before>package testutil

import (
	"fmt"
	"os"

	"github.com/RichardKnop/go-fixtures"
	"github.com/RichardKnop/example-api/util/migrations"
	"github.com/jinzhu/gorm"
	// Drivers
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

// CreateTestDatabase recreates the test database and
// runs migrations and fixtures as passed in, returning
// a pointer to the database
func CreateTestDatabase(dbPath string, migrationFunctions []func(*gorm.DB) error, fixtureFiles []string) (*gorm.DB, error) {

	// Init in-memory test database
	inMemoryDB, err := rebuildDatabase(dbPath)
	if err != nil {
		return nil, err
	}

	// Run all migrations
	migrations.MigrateAll(inMemoryDB, migrationFunctions)

	// Load data from data
	if err = fixtures.LoadFiles(fixtureFiles, inMemoryDB.DB(), "sqlite"); err != nil {
		return nil, err
	}

	return inMemoryDB, nil
}

// CreateTestDatabasePostgres is similar to CreateTestDatabase but it uses
// Postgres instead of sqlite, this is needed for testing packages that rely
// on some Postgres specifuc features (such as table inheritance)
func CreateTestDatabasePostgres(dbHost, dbUser, dbName string, migrationFunctions []func(*gorm.DB) error, fixtureFiles []string) (*gorm.DB, error) {

	// Postgres test database
	db, err := rebuildDatabasePostgres(dbHost, dbUser, dbName)
	if err != nil {
		return nil, err
	}

	// Run all migrations
	migrations.MigrateAll(db, migrationFunctions)

	// Load data from data
	if err = fixtures.LoadFiles(fixtureFiles, db.DB(), "postgres"); err != nil {
		return nil, err
	}

	return db, nil
}

// rebuildDatabase attempts to delete an existing in memory
// database and rebuild it, returning a pointer to it
func rebuildDatabase(dbPath string) (*gorm.DB, error) {
	// Delete the current database if it exists
	os.Remove(dbPath)

	// Init a new in-memory test database connection
	inMemoryDB, err := gorm.Open("sqlite3", dbPath)
	if err != nil {
		return nil, err
	}
	return inMemoryDB, nil
}

// rebuildDatabase attempts to delete an existing Postgres
// database and rebuild it, returning a pointer to it
func rebuildDatabasePostgres(dbHost, dbUser, dbName string) (*gorm.DB, error) {
	db, err := openPostgresDB(dbHost, dbUser, "template1")
	if err != nil {
		return nil, err
	}

	if err := db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s;", dbName)).Error; err != nil {
		return nil, err
	}

	if err := db.Exec(fmt.Sprintf("CREATE DATABASE %s;", dbName)).Error; err != nil {
		return nil, err
	}

	return openPostgresDB(dbHost, dbUser, dbName)
}

func openPostgresDB(dbHost, dbUser, dbName string) (*gorm.DB, error) {
	// Init a new postgres test database connection
	db, err := gorm.Open("postgres",
		fmt.Sprintf(
			"sslmode=disable host=%s port=5432 user=%s password='' dbname=%s",
			dbHost,
			dbUser,
			dbName,
		),
	)
	if err != nil {
		return nil, err
	}
	return db, nil
}
<commit_msg>Fixed linter issue.<commit_after>package testutil

import (
	"fmt"
	"os"

	"github.com/RichardKnop/example-api/util/migrations"
	"github.com/RichardKnop/go-fixtures"
	"github.com/jinzhu/gorm"
	// Drivers
	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

// CreateTestDatabase recreates the test database and
// runs migrations and fixtures as passed in, returning
// a pointer to the database
func CreateTestDatabase(dbPath string, migrationFunctions []func(*gorm.DB) error, fixtureFiles []string) (*gorm.DB, error) {

	// Init in-memory test database
	inMemoryDB, err := rebuildDatabase(dbPath)
	if err != nil {
		return nil, err
	}

	// Run all migrations
	migrations.MigrateAll(inMemoryDB, migrationFunctions)

	// Load data from data
	if err = fixtures.LoadFiles(fixtureFiles, inMemoryDB.DB(), "sqlite"); err != nil {
		return nil, err
	}

	return inMemoryDB, nil
}

// CreateTestDatabasePostgres is similar to CreateTestDatabase but it uses
// Postgres instead of sqlite, this is needed for testing packages that rely
// on some Postgres specifuc features (such as table inheritance)
func CreateTestDatabasePostgres(dbHost, dbUser, dbName string, migrationFunctions []func(*gorm.DB) error, fixtureFiles []string) (*gorm.DB, error) {

	// Postgres test database
	db, err := rebuildDatabasePostgres(dbHost, dbUser, dbName)
	if err != nil {
		return nil, err
	}

	// Run all migrations
	migrations.MigrateAll(db, migrationFunctions)

	// Load data from data
	if err = fixtures.LoadFiles(fixtureFiles, db.DB(), "postgres"); err != nil {
		return nil, err
	}

	return db, nil
}

// rebuildDatabase attempts to delete an existing in memory
// database and rebuild it, returning a pointer to it
func rebuildDatabase(dbPath string) (*gorm.DB, error) {
	// Delete the current database if it exists
	os.Remove(dbPath)

	// Init a new in-memory test database connection
	inMemoryDB, err := gorm.Open("sqlite3", dbPath)
	if err != nil {
		return nil, err
	}
	return inMemoryDB, nil
}

// rebuildDatabase attempts to delete an existing Postgres
// database and rebuild it, returning a pointer to it
func rebuildDatabasePostgres(dbHost, dbUser, dbName string) (*gorm.DB, error) {
	db, err := openPostgresDB(dbHost, dbUser, "template1")
	if err != nil {
		return nil, err
	}

	if err := db.Exec(fmt.Sprintf("DROP DATABASE IF EXISTS %s;", dbName)).Error; err != nil {
		return nil, err
	}

	if err := db.Exec(fmt.Sprintf("CREATE DATABASE %s;", dbName)).Error; err != nil {
		return nil, err
	}

	return openPostgresDB(dbHost, dbUser, dbName)
}

func openPostgresDB(dbHost, dbUser, dbName string) (*gorm.DB, error) {
	// Init a new postgres test database connection
	db, err := gorm.Open("postgres",
		fmt.Sprintf(
			"sslmode=disable host=%s port=5432 user=%s password='' dbname=%s",
			dbHost,
			dbUser,
			dbName,
		),
	)
	if err != nil {
		return nil, err
	}
	return db, nil
}
<|endoftext|>


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

// Package canvas defines the canvas that the widgets draw on.
package canvas

import (
	"fmt"
	"image"

	"github.com/mum4k/termdash/area"
	"github.com/mum4k/termdash/cell"
	"github.com/mum4k/termdash/terminalapi"
)

// Canvas is where a widget draws its output for display on the terminal.
type Canvas struct {
	// area is the area the buffer was created for.
	// Contains absolute coordinates on the target terminal, while the buffer
	// contains relative zero-based coordinates for this canvas.
	area image.Rectangle

	// buffer is where the drawing happens.
	buffer cell.Buffer
}

// New returns a new Canvas with a buffer for the provided area.
func New(ar image.Rectangle) (*Canvas, error) {
	if ar.Min.X < 0 || ar.Min.Y < 0 || ar.Max.X < 0 || ar.Max.Y < 0 {
		return nil, fmt.Errorf("area cannot start or end on the negative axis, got: %+v", ar)
	}

	b, err := cell.NewBuffer(area.Size(ar))
	if err != nil {
		return nil, err
	}
	return &Canvas{
		area:   ar,
		buffer: b,
	}, nil
}

// Size returns the size of the 2-D canvas.
func (c *Canvas) Size() image.Point {
	return c.buffer.Size()
}

// Area returns the area of the 2-D canvas.
func (c *Canvas) Area() image.Rectangle {
	s := c.buffer.Size()
	return image.Rect(0, 0, s.X, s.Y)
}

// Clear clears all the content on the canvas.
func (c *Canvas) Clear() error {
	b, err := cell.NewBuffer(c.Size())
	if err != nil {
		return err
	}
	c.buffer = b
	return nil
}

// SetCell sets the rune of the specified cell on the canvas. Returns the
// number of cells the rune occupies, wide runes can occupy multiple cells when
// printed on the terminal. See http://www.unicode.org/reports/tr11/.
// Use the options to specify which attributes to modify, if an attribute
// option isn't specified, the attribute retains its previous value.
func (c *Canvas) SetCell(p image.Point, r rune, opts ...cell.Option) (int, error) {
	return c.buffer.SetCell(p, r, opts...)
}

// Cell returns a copy of the specified cell.
func (c *Canvas) Cell(p image.Point) (*cell.Cell, error) {
	ar, err := area.FromSize(c.Size())
	if err != nil {
		return nil, err
	}
	if !p.In(ar) {
		return nil, fmt.Errorf("point %v falls outside of the area %v occupied by the canvas", p, ar)
	}

	return c.buffer[p.X][p.Y].Copy(), nil
}

// setCellFunc is a function that sets cell content on a terminal or a canvas.
type setCellFunc func(image.Point, rune, ...cell.Option) error

// copyTo is the internal implementation of code that copies the content of a
// canvas. If a on zero offset is provided, all the copied points are offset by
// this amount.
// The dstSetCell function is called for every point in this canvas when
// copying it to the destination.
func (c *Canvas) copyTo(offset image.Point, dstSetCell setCellFunc) error {
	for col := range c.buffer {
		for row := range c.buffer[col] {
			partial, err := c.buffer.IsPartial(image.Point{col, row})
			if err != nil {
				return err
			}
			if partial {
				// Skip over partial cells, i.e. cells that follow a cell
				// containing a full-width rune. A full-width rune takes only
				// one cell in the buffer, but two on the terminal.
				// See http://www.unicode.org/reports/tr11/.
				continue
			}
			cell := c.buffer[col][row]
			p := image.Point{col, row}.Add(offset)
			if err := dstSetCell(p, cell.Rune, cell.Opts); err != nil {
				return fmt.Errorf("setCellFunc%v => error: %v", p, err)
			}
		}
	}
	return nil
}

// Apply applies the canvas to the corresponding area of the terminal.
// Guarantees to stay within limits of the area the canvas was created with.
func (c *Canvas) Apply(t terminalapi.Terminal) error {
	termArea, err := area.FromSize(t.Size())
	if err != nil {
		return err
	}

	bufArea, err := area.FromSize(c.buffer.Size())
	if err != nil {
		return err
	}

	if !bufArea.In(termArea) {
		return fmt.Errorf("the canvas area %+v doesn't fit onto the terminal %+v", bufArea, termArea)
	}

	// The image.Point{0, 0} of this canvas isn't always exactly at
	// image.Point{0, 0} on the terminal.
	// Depends on area assigned by the container.
	offset := c.area.Min
	return c.copyTo(offset, t.SetCell)
}

// CopyTo copies the content of this canvas onto the destination canvas.
// This canvas can have an offset when compared to the destination canvas, i.e.
// the area of this canvas doesn't have to be zero-based.
func (c *Canvas) CopyTo(dst *Canvas) error {
	if !c.area.In(dst.Area()) {
		return fmt.Errorf("the canvas area %v doesn't fit or lie inside the destination canvas area %v", c.area, dst.Area())
	}

	fn := setCellFunc(func(p image.Point, r rune, opts ...cell.Option) error {
		if _, err := dst.SetCell(p, r, opts...); err != nil {
			return fmt.Errorf("dst.SetCell => %v", err)
		}
		return nil
	})

	// Neither of the two canvases (source and destination) have to be zero
	// based. Canvas is not zero based if it is positioned elsewhere, i.e.
	// providing a smaller view of another canvas.
	// E.g. a widget can assign a smaller portion of its canvas to a component
	// and in order to restrict drawing of this component to a smaller area, it
	// can create a sub-canvas. This sub-canvas can be smaller and have a
	// specific starting position other than image.Point{0, 0}.
	// Copying this sub-canvas back onto the parent accounts for this offset.
	//offset := c.area.Min.Sub(dst.area.Min)
	offset := c.area.Min
	return c.copyTo(offset, fn)
}
<commit_msg>Self-review.<commit_after>// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package canvas defines the canvas that the widgets draw on.
package canvas

import (
	"fmt"
	"image"

	"github.com/mum4k/termdash/area"
	"github.com/mum4k/termdash/cell"
	"github.com/mum4k/termdash/terminalapi"
)

// Canvas is where a widget draws its output for display on the terminal.
type Canvas struct {
	// area is the area the buffer was created for.
	// Contains absolute coordinates on the target terminal, while the buffer
	// contains relative zero-based coordinates for this canvas.
	area image.Rectangle

	// buffer is where the drawing happens.
	buffer cell.Buffer
}

// New returns a new Canvas with a buffer for the provided area.
func New(ar image.Rectangle) (*Canvas, error) {
	if ar.Min.X < 0 || ar.Min.Y < 0 || ar.Max.X < 0 || ar.Max.Y < 0 {
		return nil, fmt.Errorf("area cannot start or end on the negative axis, got: %+v", ar)
	}

	b, err := cell.NewBuffer(area.Size(ar))
	if err != nil {
		return nil, err
	}
	return &Canvas{
		area:   ar,
		buffer: b,
	}, nil
}

// Size returns the size of the 2-D canvas.
func (c *Canvas) Size() image.Point {
	return c.buffer.Size()
}

// Area returns the area of the 2-D canvas.
func (c *Canvas) Area() image.Rectangle {
	s := c.buffer.Size()
	return image.Rect(0, 0, s.X, s.Y)
}

// Clear clears all the content on the canvas.
func (c *Canvas) Clear() error {
	b, err := cell.NewBuffer(c.Size())
	if err != nil {
		return err
	}
	c.buffer = b
	return nil
}

// SetCell sets the rune of the specified cell on the canvas. Returns the
// number of cells the rune occupies, wide runes can occupy multiple cells when
// printed on the terminal. See http://www.unicode.org/reports/tr11/.
// Use the options to specify which attributes to modify, if an attribute
// option isn't specified, the attribute retains its previous value.
func (c *Canvas) SetCell(p image.Point, r rune, opts ...cell.Option) (int, error) {
	return c.buffer.SetCell(p, r, opts...)
}

// Cell returns a copy of the specified cell.
func (c *Canvas) Cell(p image.Point) (*cell.Cell, error) {
	ar, err := area.FromSize(c.Size())
	if err != nil {
		return nil, err
	}
	if !p.In(ar) {
		return nil, fmt.Errorf("point %v falls outside of the area %v occupied by the canvas", p, ar)
	}

	return c.buffer[p.X][p.Y].Copy(), nil
}

// setCellFunc is a function that sets cell content on a terminal or a canvas.
type setCellFunc func(image.Point, rune, ...cell.Option) error

// copyTo is the internal implementation of code that copies the content of a
// canvas. If a non zero offset is provided, all the copied points are offset by
// this amount.
// The dstSetCell function is called for every point in this canvas when
// copying it to the destination.
func (c *Canvas) copyTo(offset image.Point, dstSetCell setCellFunc) error {
	for col := range c.buffer {
		for row := range c.buffer[col] {
			partial, err := c.buffer.IsPartial(image.Point{col, row})
			if err != nil {
				return err
			}
			if partial {
				// Skip over partial cells, i.e. cells that follow a cell
				// containing a full-width rune. A full-width rune takes only
				// one cell in the buffer, but two on the terminal.
				// See http://www.unicode.org/reports/tr11/.
				continue
			}
			cell := c.buffer[col][row]
			p := image.Point{col, row}.Add(offset)
			if err := dstSetCell(p, cell.Rune, cell.Opts); err != nil {
				return fmt.Errorf("setCellFunc%v => error: %v", p, err)
			}
		}
	}
	return nil
}

// Apply applies the canvas to the corresponding area of the terminal.
// Guarantees to stay within limits of the area the canvas was created with.
func (c *Canvas) Apply(t terminalapi.Terminal) error {
	termArea, err := area.FromSize(t.Size())
	if err != nil {
		return err
	}

	bufArea, err := area.FromSize(c.buffer.Size())
	if err != nil {
		return err
	}

	if !bufArea.In(termArea) {
		return fmt.Errorf("the canvas area %+v doesn't fit onto the terminal %+v", bufArea, termArea)
	}

	// The image.Point{0, 0} of this canvas isn't always exactly at
	// image.Point{0, 0} on the terminal.
	// Depends on area assigned by the container.
	offset := c.area.Min
	return c.copyTo(offset, t.SetCell)
}

// CopyTo copies the content of this canvas onto the destination canvas.
// This canvas can have an offset when compared to the destination canvas, i.e.
// the area of this canvas doesn't have to be zero-based.
func (c *Canvas) CopyTo(dst *Canvas) error {
	if !c.area.In(dst.Area()) {
		return fmt.Errorf("the canvas area %v doesn't fit or lie inside the destination canvas area %v", c.area, dst.Area())
	}

	fn := setCellFunc(func(p image.Point, r rune, opts ...cell.Option) error {
		if _, err := dst.SetCell(p, r, opts...); err != nil {
			return fmt.Errorf("dst.SetCell => %v", err)
		}
		return nil
	})

	// Neither of the two canvases (source and destination) have to be zero
	// based. Canvas is not zero based if it is positioned elsewhere, i.e.
	// providing a smaller view of another canvas.
	// E.g. a widget can assign a smaller portion of its canvas to a component
	// in order to restrict drawing of this component to a smaller area. To do
	// this it can create a sub-canvas. This sub-canvas can have a specific
	// starting position other than image.Point{0, 0} relative to the parent
	// canvas. Copying this sub-canvas back onto the parent accounts for this
	// offset.
	offset := c.area.Min
	return c.copyTo(offset, fn)
}
<|endoftext|>