<commit_before>package filer

import (
	"context"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
	"github.com/golang/groupcache/singleflight"
	"io"
	"math/rand"
	"sync"
	"time"
)

var (
	ReadWaitTime = 6 * time.Second
)

type ChunkReadAt struct {
	masterClient *wdclient.MasterClient
	chunkViews   []*ChunkView
	lookupFileId LookupFileIdFunctionType
	readerLock   sync.Mutex
	fileSize     int64

	fetchGroup      singleflight.Group
	lastChunkFileId string
	lastChunkData   []byte
	chunkCache      chunk_cache.ChunkCache
}

// var _ = io.ReaderAt(&ChunkReadAt{})

type LookupFileIdFunctionType func(fileId string) (targetUrls []string, err error)

func LookupFn(filerClient filer_pb.FilerClient) LookupFileIdFunctionType {

	vidCache := make(map[string]*filer_pb.Locations)
	return func(fileId string) (targetUrls []string, err error) {
		vid := VolumeId(fileId)
		locations, found := vidCache[vid]

		waitTime := time.Second
		for !found && waitTime < ReadWaitTime {
			// println("looking up volume", vid)
			err = filerClient.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
				resp, err := client.LookupVolume(context.Background(), &filer_pb.LookupVolumeRequest{
					VolumeIds: []string{vid},
				})
				if err != nil {
					return err
				}

				locations = resp.LocationsMap[vid]
				if locations == nil || len(locations.Locations) == 0 {
					glog.V(0).Infof("failed to locate %s", fileId)
					return fmt.Errorf("failed to locate %s", fileId)
				}
				vidCache[vid] = locations

				return nil
			})
			if err == nil {
				break
			}
			glog.V(1).Infof("wait for volume %s", vid)
			time.Sleep(waitTime)
			waitTime += waitTime / 2
		}

		if err != nil {
			return nil, err
		}

		for _, loc := range locations.Locations {
			volumeServerAddress := filerClient.AdjustedUrl(loc)
			targetUrl := fmt.Sprintf("http://%s/%s", volumeServerAddress, fileId)
			targetUrls = append(targetUrls, targetUrl)
		}

		for i := len(targetUrls) - 1; i > 0; i-- {
			j := rand.Intn(i + 1)
			targetUrls[i], targetUrls[j] = targetUrls[j], targetUrls[i]
		}

		return
	}
}

func NewChunkReaderAtFromClient(filerClient filer_pb.FilerClient, chunkViews []*ChunkView, chunkCache chunk_cache.ChunkCache, fileSize int64) *ChunkReadAt {

	return &ChunkReadAt{
		chunkViews:   chunkViews,
		lookupFileId: LookupFn(filerClient),
		chunkCache:   chunkCache,
		fileSize:     fileSize,
	}
}

func (c *ChunkReadAt) ReadAt(p []byte, offset int64) (n int, err error) {

	c.readerLock.Lock()
	defer c.readerLock.Unlock()

	glog.V(4).Infof("ReadAt [%d,%d) of total file size %d bytes %d chunk views", offset, offset+int64(len(p)), c.fileSize, len(c.chunkViews))
	return c.doReadAt(p[n:], offset+int64(n))
}

func (c *ChunkReadAt) doReadAt(p []byte, offset int64) (n int, err error) {

	var buffer []byte
	startOffset, remaining := offset, int64(len(p))
	var nextChunk *ChunkView
	for i, chunk := range c.chunkViews {
		if remaining <= 0 {
			break
		}
		if i+1 < len(c.chunkViews) {
			nextChunk = c.chunkViews[i+1]
		} else {
			nextChunk = nil
		}
		if startOffset < chunk.LogicOffset {
			gap := int(chunk.LogicOffset - startOffset)
			glog.V(4).Infof("zero [%d,%d)", startOffset, startOffset+int64(gap))
			n += int(min(int64(gap), remaining))
			startOffset, remaining = chunk.LogicOffset, remaining-int64(gap)
			if remaining <= 0 {
				break
			}
		}
		// fmt.Printf(">>> doReadAt [%d,%d), chunk[%d,%d)\n", offset, offset+int64(len(p)), chunk.LogicOffset, chunk.LogicOffset+int64(chunk.Size))
		chunkStart, chunkStop := max(chunk.LogicOffset, startOffset), min(chunk.LogicOffset+int64(chunk.Size), startOffset+remaining)
		if chunkStart >= chunkStop {
			continue
		}
		glog.V(4).Infof("read [%d,%d), %d/%d chunk %s [%d,%d)", chunkStart, chunkStop, i, len(c.chunkViews), chunk.FileId, chunk.LogicOffset-chunk.Offset, chunk.LogicOffset-chunk.Offset+int64(chunk.Size))
		buffer, err = c.readFromWholeChunkData(chunk, nextChunk)
		if err != nil {
			glog.Errorf("fetching chunk %+v: %v\n", chunk, err)
			return
		}
		bufferOffset := chunkStart - chunk.LogicOffset + chunk.Offset
		copied := copy(p[startOffset-offset:chunkStop-chunkStart+startOffset-offset], buffer[bufferOffset:bufferOffset+chunkStop-chunkStart])
		n += copied
		startOffset, remaining = startOffset+int64(copied), remaining-int64(copied)
	}

	glog.V(4).Infof("doReadAt [%d,%d), n:%v, err:%v", offset, offset+int64(len(p)), n, err)

	if err == nil && remaining > 0 && c.fileSize > startOffset {
		delta := int(min(remaining, c.fileSize-startOffset))
		glog.V(4).Infof("zero2 [%d,%d) of file size %d bytes", startOffset, startOffset+int64(delta), c.fileSize)
		n += delta
	}

	if err == nil && offset+int64(len(p)) > c.fileSize {
		err = io.EOF
	}
	// fmt.Printf("~~~ filled %d, err: %v\n\n", n, err)

	return

}

func (c *ChunkReadAt) readFromWholeChunkData(chunkView *ChunkView, nextChunkViews ...*ChunkView) (chunkData []byte, err error) {

	if c.lastChunkFileId == chunkView.FileId {
		return c.lastChunkData, nil
	}

	v, doErr := c.readOneWholeChunk(chunkView)

	if doErr != nil {
		return nil, doErr
	}

	chunkData = v.([]byte)

	c.lastChunkData = chunkData
	c.lastChunkFileId = chunkView.FileId

	for _, nextChunkView := range nextChunkViews {
		if c.chunkCache != nil && nextChunkView != nil {
			go c.readOneWholeChunk(nextChunkView)
		}
	}

	return
}

func (c *ChunkReadAt) readOneWholeChunk(chunkView *ChunkView) (interface{}, error) {

	var err error

	return c.fetchGroup.Do(chunkView.FileId, func() (interface{}, error) {

		glog.V(4).Infof("readFromWholeChunkData %s offset %d [%d,%d) size at least %d", chunkView.FileId, chunkView.Offset, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size), chunkView.ChunkSize)

		data := c.chunkCache.GetChunk(chunkView.FileId, chunkView.ChunkSize)
		if data != nil {
			glog.V(4).Infof("cache hit %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset-chunkView.Offset, chunkView.LogicOffset-chunkView.Offset+int64(len(data)))
		} else {
			var err error
			data, err = c.doFetchFullChunkData(chunkView)
			if err != nil {
				return data, err
			}
			c.chunkCache.SetChunk(chunkView.FileId, data)
		}
		return data, err
	})
}

func (c *ChunkReadAt) doFetchFullChunkData(chunkView *ChunkView) ([]byte, error) {

	glog.V(4).Infof("+ doFetchFullChunkData %s", chunkView.FileId)

	data, err := fetchChunk(c.lookupFileId, chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped)

	glog.V(4).Infof("- doFetchFullChunkData %s", chunkView.FileId)

	return data, err

}
<commit_msg>file read report EOF<commit_after>package filer

import (
	"context"
	"fmt"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/util/chunk_cache"
	"github.com/chrislusf/seaweedfs/weed/wdclient"
	"github.com/golang/groupcache/singleflight"
	"io"
	"math/rand"
	"sync"
	"time"
)

var (
	ReadWaitTime = 6 * time.Second
)

type ChunkReadAt struct {
	masterClient *wdclient.MasterClient
	chunkViews   []*ChunkView
	lookupFileId LookupFileIdFunctionType
	readerLock   sync.Mutex
	fileSize     int64

	fetchGroup      singleflight.Group
	lastChunkFileId string
	lastChunkData   []byte
	chunkCache      chunk_cache.ChunkCache
}

// var _ = io.ReaderAt(&ChunkReadAt{})

type LookupFileIdFunctionType func(fileId string) (targetUrls []string, err error)

func LookupFn(filerClient filer_pb.FilerClient) LookupFileIdFunctionType {

	vidCache := make(map[string]*filer_pb.Locations)
	return func(fileId string) (targetUrls []string, err error) {
		vid := VolumeId(fileId)
		locations, found := vidCache[vid]

		waitTime := time.Second
		for !found && waitTime < ReadWaitTime {
			// println("looking up volume", vid)
			err = filerClient.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {
				resp, err := client.LookupVolume(context.Background(), &filer_pb.LookupVolumeRequest{
					VolumeIds: []string{vid},
				})
				if err != nil {
					return err
				}

				locations = resp.LocationsMap[vid]
				if locations == nil || len(locations.Locations) == 0 {
					glog.V(0).Infof("failed to locate %s", fileId)
					return fmt.Errorf("failed to locate %s", fileId)
				}
				vidCache[vid] = locations

				return nil
			})
			if err == nil {
				break
			}
			glog.V(1).Infof("wait for volume %s", vid)
			time.Sleep(waitTime)
			waitTime += waitTime / 2
		}

		if err != nil {
			return nil, err
		}

		for _, loc := range locations.Locations {
			volumeServerAddress := filerClient.AdjustedUrl(loc)
			targetUrl := fmt.Sprintf("http://%s/%s", volumeServerAddress, fileId)
			targetUrls = append(targetUrls, targetUrl)
		}

		for i := len(targetUrls) - 1; i > 0; i-- {
			j := rand.Intn(i + 1)
			targetUrls[i], targetUrls[j] = targetUrls[j], targetUrls[i]
		}

		return
	}
}

func NewChunkReaderAtFromClient(filerClient filer_pb.FilerClient, chunkViews []*ChunkView, chunkCache chunk_cache.ChunkCache, fileSize int64) *ChunkReadAt {

	return &ChunkReadAt{
		chunkViews:   chunkViews,
		lookupFileId: LookupFn(filerClient),
		chunkCache:   chunkCache,
		fileSize:     fileSize,
	}
}

func (c *ChunkReadAt) ReadAt(p []byte, offset int64) (n int, err error) {

	c.readerLock.Lock()
	defer c.readerLock.Unlock()

	glog.V(4).Infof("ReadAt [%d,%d) of total file size %d bytes %d chunk views", offset, offset+int64(len(p)), c.fileSize, len(c.chunkViews))
	return c.doReadAt(p[n:], offset+int64(n))
}

func (c *ChunkReadAt) doReadAt(p []byte, offset int64) (n int, err error) {

	var buffer []byte
	startOffset, remaining := offset, int64(len(p))
	var nextChunk *ChunkView
	for i, chunk := range c.chunkViews {
		if remaining <= 0 {
			break
		}
		if i+1 < len(c.chunkViews) {
			nextChunk = c.chunkViews[i+1]
		} else {
			nextChunk = nil
		}
		if startOffset < chunk.LogicOffset {
			gap := int(chunk.LogicOffset - startOffset)
			glog.V(4).Infof("zero [%d,%d)", startOffset, startOffset+int64(gap))
			n += int(min(int64(gap), remaining))
			startOffset, remaining = chunk.LogicOffset, remaining-int64(gap)
			if remaining <= 0 {
				break
			}
		}
		// fmt.Printf(">>> doReadAt [%d,%d), chunk[%d,%d)\n", offset, offset+int64(len(p)), chunk.LogicOffset, chunk.LogicOffset+int64(chunk.Size))
		chunkStart, chunkStop := max(chunk.LogicOffset, startOffset), min(chunk.LogicOffset+int64(chunk.Size), startOffset+remaining)
		if chunkStart >= chunkStop {
			continue
		}
		glog.V(4).Infof("read [%d,%d), %d/%d chunk %s [%d,%d)", chunkStart, chunkStop, i, len(c.chunkViews), chunk.FileId, chunk.LogicOffset-chunk.Offset, chunk.LogicOffset-chunk.Offset+int64(chunk.Size))
		buffer, err = c.readFromWholeChunkData(chunk, nextChunk)
		if err != nil {
			glog.Errorf("fetching chunk %+v: %v\n", chunk, err)
			return
		}
		bufferOffset := chunkStart - chunk.LogicOffset + chunk.Offset
		copied := copy(p[startOffset-offset:chunkStop-chunkStart+startOffset-offset], buffer[bufferOffset:bufferOffset+chunkStop-chunkStart])
		n += copied
		startOffset, remaining = startOffset+int64(copied), remaining-int64(copied)
	}

	glog.V(4).Infof("doReadAt [%d,%d), n:%v, err:%v", offset, offset+int64(len(p)), n, err)

	if err == nil && remaining > 0 && c.fileSize > startOffset {
		delta := int(min(remaining, c.fileSize-startOffset))
		glog.V(4).Infof("zero2 [%d,%d) of file size %d bytes", startOffset, startOffset+int64(delta), c.fileSize)
		n += delta
	}

	if err == nil && offset+int64(len(p)) >= c.fileSize {
		err = io.EOF
	}
	// fmt.Printf("~~~ filled %d, err: %v\n\n", n, err)

	return

}

func (c *ChunkReadAt) readFromWholeChunkData(chunkView *ChunkView, nextChunkViews ...*ChunkView) (chunkData []byte, err error) {

	if c.lastChunkFileId == chunkView.FileId {
		return c.lastChunkData, nil
	}

	v, doErr := c.readOneWholeChunk(chunkView)

	if doErr != nil {
		return nil, doErr
	}

	chunkData = v.([]byte)

	c.lastChunkData = chunkData
	c.lastChunkFileId = chunkView.FileId

	for _, nextChunkView := range nextChunkViews {
		if c.chunkCache != nil && nextChunkView != nil {
			go c.readOneWholeChunk(nextChunkView)
		}
	}

	return
}

func (c *ChunkReadAt) readOneWholeChunk(chunkView *ChunkView) (interface{}, error) {

	var err error

	return c.fetchGroup.Do(chunkView.FileId, func() (interface{}, error) {

		glog.V(4).Infof("readFromWholeChunkData %s offset %d [%d,%d) size at least %d", chunkView.FileId, chunkView.Offset, chunkView.LogicOffset, chunkView.LogicOffset+int64(chunkView.Size), chunkView.ChunkSize)

		data := c.chunkCache.GetChunk(chunkView.FileId, chunkView.ChunkSize)
		if data != nil {
			glog.V(4).Infof("cache hit %s [%d,%d)", chunkView.FileId, chunkView.LogicOffset-chunkView.Offset, chunkView.LogicOffset-chunkView.Offset+int64(len(data)))
		} else {
			var err error
			data, err = c.doFetchFullChunkData(chunkView)
			if err != nil {
				return data, err
			}
			c.chunkCache.SetChunk(chunkView.FileId, data)
		}
		return data, err
	})
}

func (c *ChunkReadAt) doFetchFullChunkData(chunkView *ChunkView) ([]byte, error) {

	glog.V(4).Infof("+ doFetchFullChunkData %s", chunkView.FileId)

	data, err := fetchChunk(c.lookupFileId, chunkView.FileId, chunkView.CipherKey, chunkView.IsGzipped)

	glog.V(4).Infof("- doFetchFullChunkData %s", chunkView.FileId)

	return data, err

}
<|endoftext|>


<commit_before>package filesys

import (
	"sync"

	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/seaweedfs/fuse/fs"
)

type FsCache struct {
	root *FsNode
}
type FsNode struct {
	parent       *FsNode
	node         fs.Node
	name         string
	childrenLock sync.RWMutex
	children     map[string]*FsNode
}

func newFsCache(root fs.Node) *FsCache {
	return &FsCache{
		root: &FsNode{
			node: root,
		},
	}
}

func (c *FsCache) GetFsNode(path util.FullPath) fs.Node {
	t := c.root
	for _, p := range path.Split() {
		t = t.findChild(p)
		if t == nil {
			return nil
		}
	}
	return t.node
}

func (c *FsCache) SetFsNode(path util.FullPath, node fs.Node) {
	t := c.root
	for _, p := range path.Split() {
		t = t.ensureChild(p)
	}
	t.node = node
}

func (c *FsCache) EnsureFsNode(path util.FullPath, genNodeFn func() fs.Node) fs.Node {
	t := c.GetFsNode(path)
	if t != nil {
		return t
	}
	t = genNodeFn()
	c.SetFsNode(path, t)
	return t
}

func (c *FsCache) DeleteFsNode(path util.FullPath) {
	t := c.root
	for _, p := range path.Split() {
		t = t.findChild(p)
		if t == nil {
			return
		}
	}
	if t.parent != nil {
		t.parent.deleteChild(t.name)
	}
	t.deleteSelf()
}

// oldPath and newPath are full path including the new name
func (c *FsCache) Move(oldPath util.FullPath, newPath util.FullPath) *FsNode {

	// find old node
	src := c.root
	for _, p := range oldPath.Split() {
		src = src.findChild(p)
		if src == nil {
			return src
		}
	}
	if src.parent != nil {
		src.parent.deleteChild(src.name)
	}
	src.parent = nil

	// find new node
	target := c.root
	for _, p := range newPath.Split() {
		target = target.ensureChild(p)
	}
	parent := target.parent
	src.name = target.name
	if dir, ok := src.node.(*Dir); ok {
		dir.name = target.name // target is not Dir, but a shortcut
	}
	parent.deleteChild(target.name)

	target.deleteSelf()

	src.connectToParent(parent)

	return src
}

func (n *FsNode) connectToParent(parent *FsNode) {
	n.parent = parent
	oldNode := parent.findChild(n.name)
	if oldNode != nil {
		oldNode.deleteSelf()
	}
	if dir, ok := n.node.(*Dir); ok {
		dir.parent = parent.node.(*Dir)
	}
	n.childrenLock.Lock()
	parent.children[n.name] = n
	n.childrenLock.Unlock()
}

func (n *FsNode) findChild(name string) *FsNode {
	n.childrenLock.RLock()
	defer n.childrenLock.RUnlock()

	child, found := n.children[name]
	if found {
		return child
	}
	return nil
}

func (n *FsNode) ensureChild(name string) *FsNode {
	n.childrenLock.Lock()
	defer n.childrenLock.Unlock()

	if n.children == nil {
		n.children = make(map[string]*FsNode)
	}
	child, found := n.children[name]
	if found {
		return child
	}
	t := &FsNode{
		parent:   n,
		node:     nil,
		name:     name,
		children: nil,
	}
	n.children[name] = t
	return t
}

func (n *FsNode) deleteChild(name string) {
	n.childrenLock.Lock()
	delete(n.children, name)
	n.childrenLock.Unlock()
}

func (n *FsNode) deleteSelf() {
	n.childrenLock.Lock()
	for _, child := range n.children {
		child.deleteSelf()
	}
	n.children = nil
	n.childrenLock.Unlock()

	n.node = nil
	n.parent = nil

}
<commit_msg>refactor<commit_after>package filesys

import (
	"sync"

	"github.com/chrislusf/seaweedfs/weed/util"
	"github.com/seaweedfs/fuse/fs"
)

type FsCache struct {
	root *FsNode
}
type FsNode struct {
	parent       *FsNode
	node         fs.Node
	name         string
	childrenLock sync.RWMutex
	children     map[string]*FsNode
}

func newFsCache(root fs.Node) *FsCache {
	return &FsCache{
		root: &FsNode{
			node: root,
		},
	}
}

func (c *FsCache) GetFsNode(path util.FullPath) fs.Node {
	t := c.root
	for _, p := range path.Split() {
		t = t.findChild(p)
		if t == nil {
			return nil
		}
	}
	return t.node
}

func (c *FsCache) SetFsNode(path util.FullPath, node fs.Node) {
	t := c.root
	for _, p := range path.Split() {
		t = t.ensureChild(p)
	}
	t.node = node
}

func (c *FsCache) EnsureFsNode(path util.FullPath, genNodeFn func() fs.Node) fs.Node {
	t := c.GetFsNode(path)
	if t != nil {
		return t
	}
	t = genNodeFn()
	c.SetFsNode(path, t)
	return t
}

func (c *FsCache) DeleteFsNode(path util.FullPath) {
	t := c.root
	for _, p := range path.Split() {
		t = t.findChild(p)
		if t == nil {
			return
		}
	}
	if t.parent != nil {
		t.parent.disconnectChild(t)
	}
	t.deleteSelf()
}

// oldPath and newPath are full path including the new name
func (c *FsCache) Move(oldPath util.FullPath, newPath util.FullPath) *FsNode {

	// find old node
	src := c.root
	for _, p := range oldPath.Split() {
		src = src.findChild(p)
		if src == nil {
			return src
		}
	}
	if src.parent != nil {
		src.parent.disconnectChild(src)
	}

	// find new node
	target := c.root
	for _, p := range newPath.Split() {
		target = target.ensureChild(p)
	}
	parent := target.parent
	src.name = target.name
	if dir, ok := src.node.(*Dir); ok {
		dir.name = target.name // target is not Dir, but a shortcut
	}
	parent.disconnectChild(target)

	target.deleteSelf()

	src.connectToParent(parent)

	return src
}

func (n *FsNode) connectToParent(parent *FsNode) {
	n.parent = parent
	oldNode := parent.findChild(n.name)
	if oldNode != nil {
		oldNode.deleteSelf()
	}
	if dir, ok := n.node.(*Dir); ok {
		dir.parent = parent.node.(*Dir)
	}
	n.childrenLock.Lock()
	parent.children[n.name] = n
	n.childrenLock.Unlock()
}

func (n *FsNode) findChild(name string) *FsNode {
	n.childrenLock.RLock()
	defer n.childrenLock.RUnlock()

	child, found := n.children[name]
	if found {
		return child
	}
	return nil
}

func (n *FsNode) ensureChild(name string) *FsNode {
	n.childrenLock.Lock()
	defer n.childrenLock.Unlock()

	if n.children == nil {
		n.children = make(map[string]*FsNode)
	}
	child, found := n.children[name]
	if found {
		return child
	}
	t := &FsNode{
		parent:   n,
		node:     nil,
		name:     name,
		children: nil,
	}
	n.children[name] = t
	return t
}

func (n *FsNode) disconnectChild(child *FsNode) {
	n.childrenLock.Lock()
	delete(n.children, child.name)
	n.childrenLock.Unlock()
	child.parent = nil
}

func (n *FsNode) deleteSelf() {
	n.childrenLock.Lock()
	for _, child := range n.children {
		child.deleteSelf()
	}
	n.children = nil
	n.childrenLock.Unlock()

	n.node = nil
	n.parent = nil

}
<|endoftext|>


<commit_before><commit_msg>Find with position<commit_after><|endoftext|>


<commit_before>package jobspec2

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclsyntax"
	hcljson "github.com/hashicorp/hcl/v2/json"
	"github.com/hashicorp/nomad/api"
)

func Parse(path string, r io.Reader) (*api.Job, error) {
	if path == "" {
		if f, ok := r.(*os.File); ok {
			path = f.Name()
		}
	}

	var buf bytes.Buffer
	_, err := io.Copy(&buf, r)
	if err != nil {
		return nil, err
	}

	return ParseWithConfig(&ParseConfig{
		Path:    path,
		Body:    buf.Bytes(),
		AllowFS: false,
		Strict:  true,
	})
}

func ParseWithConfig(args *ParseConfig) (*api.Job, error) {
	args.normalize()

	c := newJobConfig(args)
	err := decode(c)
	if err != nil {
		return nil, err
	}

	normalizeJob(c)
	return c.Job, nil
}

type ParseConfig struct {
	Path    string
	BaseDir string

	// Body is the HCL body
	Body []byte

	// AllowFS enables HCL functions that require file system accecss
	AllowFS bool

	// ArgVars is the CLI -var arguments
	ArgVars []string

	// VarFiles is the paths of variable data files
	VarFiles []string

	// Envs represent process environment variable
	Envs []string

	Strict bool

	// parsedVarFiles represent parsed HCL AST of the passed EnvVars
	parsedVarFiles []*hcl.File
}

func (c *ParseConfig) normalize() {
	if c.BaseDir == "" {
		c.BaseDir = filepath.Dir(c.Path)
	}
}

func decode(c *jobConfig) error {
	var file *hcl.File
	var diags hcl.Diagnostics

	pc := c.ParseConfig

	if !isJSON(pc.Body) {
		file, diags = hclsyntax.ParseConfig(pc.Body, pc.Path, hcl.Pos{Line: 1, Column: 1})
	} else {
		file, diags = hcljson.Parse(pc.Body, pc.Path)

	}

	parsedVarFiles, mdiags := parseVarFiles(pc.VarFiles)
	pc.parsedVarFiles = parsedVarFiles
	diags = append(diags, mdiags...)

	if diags.HasErrors() {
		return diags
	}

	diags = c.decodeBody(file.Body)
	if diags.HasErrors() {
		var str strings.Builder
		for i, diag := range diags {
			if i != 0 {
				str.WriteByte('\n')
			}
			str.WriteString(diag.Error())
		}
		return errors.New(str.String())
	}
	diags = append(diags, decodeMapInterfaceType(&c.Job, c.EvalContext())...)
	diags = append(diags, decodeMapInterfaceType(&c.Tasks, c.EvalContext())...)
	diags = append(diags, decodeMapInterfaceType(&c.Vault, c.EvalContext())...)
	return nil
}

func parseVarFiles(paths []string) ([]*hcl.File, hcl.Diagnostics) {
	if len(paths) == 0 {
		return nil, nil
	}

	files := make([]*hcl.File, 0, len(paths))
	var diags hcl.Diagnostics

	for _, p := range paths {
		body, err := ioutil.ReadFile(p)
		if err != nil {
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Failed to read file",
				Detail:   fmt.Sprintf("failed to read %q: %v", p, err),
			})
			continue
		}

		var file *hcl.File
		var mdiags hcl.Diagnostics
		if !isJSON(body) {
			file, mdiags = hclsyntax.ParseConfig(body, p, hcl.Pos{Line: 1, Column: 1})
		} else {
			file, mdiags = hcljson.Parse(body, p)

		}

		files = append(files, file)
		diags = append(diags, mdiags...)
	}

	return files, diags
}

func isJSON(src []byte) bool {
	for _, c := range src {
		if c == ' ' {
			continue
		}

		return c == '{'
	}
	return false
}
<commit_msg>jobspec2: decode returns error in more cases (#9613)<commit_after>package jobspec2

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/hclsyntax"
	hcljson "github.com/hashicorp/hcl/v2/json"
	"github.com/hashicorp/nomad/api"
)

func Parse(path string, r io.Reader) (*api.Job, error) {
	if path == "" {
		if f, ok := r.(*os.File); ok {
			path = f.Name()
		}
	}

	var buf bytes.Buffer
	_, err := io.Copy(&buf, r)
	if err != nil {
		return nil, err
	}

	return ParseWithConfig(&ParseConfig{
		Path:    path,
		Body:    buf.Bytes(),
		AllowFS: false,
		Strict:  true,
	})
}

func ParseWithConfig(args *ParseConfig) (*api.Job, error) {
	args.normalize()

	c := newJobConfig(args)
	err := decode(c)
	if err != nil {
		return nil, err
	}

	normalizeJob(c)
	return c.Job, nil
}

type ParseConfig struct {
	Path    string
	BaseDir string

	// Body is the HCL body
	Body []byte

	// AllowFS enables HCL functions that require file system accecss
	AllowFS bool

	// ArgVars is the CLI -var arguments
	ArgVars []string

	// VarFiles is the paths of variable data files
	VarFiles []string

	// Envs represent process environment variable
	Envs []string

	Strict bool

	// parsedVarFiles represent parsed HCL AST of the passed EnvVars
	parsedVarFiles []*hcl.File
}

func (c *ParseConfig) normalize() {
	if c.BaseDir == "" {
		c.BaseDir = filepath.Dir(c.Path)
	}
}

func decode(c *jobConfig) error {
	config := c.ParseConfig

	file, diags := parseHCLOrJSON(config.Body, config.Path)

	for _, varFile := range config.VarFiles {
		parsedVarFile, ds := parseFile(varFile)
		config.parsedVarFiles = append(config.parsedVarFiles, parsedVarFile)
		diags = append(diags, ds...)
	}

	diags = append(diags, c.decodeBody(file.Body)...)

	if diags.HasErrors() {
		var str strings.Builder
		for i, diag := range diags {
			if i != 0 {
				str.WriteByte('\n')
			}
			str.WriteString(diag.Error())
		}
		return errors.New(str.String())
	}

	diags = append(diags, decodeMapInterfaceType(&c.Job, c.EvalContext())...)
	diags = append(diags, decodeMapInterfaceType(&c.Tasks, c.EvalContext())...)
	diags = append(diags, decodeMapInterfaceType(&c.Vault, c.EvalContext())...)

	if diags.HasErrors() {
		return diags
	}

	return nil
}

func parseFile(path string) (*hcl.File, hcl.Diagnostics) {
	body, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, hcl.Diagnostics{
			&hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Failed to read file",
				Detail:   fmt.Sprintf("failed to read %q: %v", path, err),
			},
		}
	}

	return parseHCLOrJSON(body, path)
}

func parseHCLOrJSON(src []byte, filename string) (*hcl.File, hcl.Diagnostics) {
	if isJSON(src) {
		return hcljson.Parse(src, filename)
	}

	return hclsyntax.ParseConfig(src, filename, hcl.Pos{Line: 1, Column: 1})
}

func isJSON(src []byte) bool {
	for _, c := range src {
		if c == ' ' {
			continue
		}

		return c == '{'
	}
	return false
}
<|endoftext|>


<commit_before>package plugins

import (
	"io/ioutil"
	"os"
	"path"

	"github.com/BurntSushi/toml"
	log "github.com/Sirupsen/logrus"
	er "github.com/maliceio/malice/malice/errors"
	"github.com/maliceio/malice/malice/maldirs"
	"github.com/maliceio/malice/malice/maldocker"
)

// Plugin represents a single plugin setting.
type Plugin struct {
	Name        string   `toml:"name"`
	Enabled     bool     `toml:"enabled"`
	Category    string   `toml:"category"`
	Description string   `toml:"description"`
	Image       string   `toml:"image"`
	APIKey      string   `toml:"apikey"`
	Mime        string   `toml:"mime"`
	HashTypes   []string `toml:"hashtypes"`
	Cmd         string   `toml:"cmd"`
	Env         []string `toml:"env"`
	Installed   bool
}

// Configuration represents the malice runtime plugins.
type Configuration struct {
	Plugins []Plugin `toml:"plugin"`
}

// Plugs represents the Malice runtime configuration
var Plugs Configuration

// Load plugins.toml into Plug var
func Load() {
	// Try to load plugins from
	// - git repo folder      : MALICE_ROOT/plugins/plugins.toml
	// - .malice folder       : $HOME/.malice/plugins.toml
	// - binary embedded file : bindata

	// Check for plugins config in repo
	if _, err := os.Stat("./plugins/plugins.toml"); err == nil {
		log.Debug("Malice plugins loaded from ./plugins/plugins.toml")

		_, err := toml.DecodeFile("./plugins/plugins.toml", &Plugs)
		er.CheckError(err)

		return
	}
	// Check for plugins config in .malice folder
	if _, err := os.Stat(path.Join(maldirs.GetBaseDir(), "./plugins.toml")); err == nil {
		homeConfigDir := path.Join(maldirs.GetBaseDir(), "./plugins.toml")
		log.Debug("Malice plugins loaded from ", homeConfigDir)

		_, err := toml.DecodeFile(homeConfigDir, &Plugs)
		er.CheckError(err)

		return
	}
	// Read plugin config out of bindata
	tomlData, err := Asset("plugins/plugins.toml")
	if err != nil {
		log.Error(err)
	}

	if _, err := toml.Decode(string(tomlData), &Plugs); err == nil {
		log.Debug("Malice plugins loaded from plugins/bindata.go")
		configPath := path.Join(maldirs.GetBaseDir(), "./plugins.toml")
		er.CheckError(ioutil.WriteFile(configPath, tomlData, 0644))
	}
	er.CheckError(err)

	return
}

func setInstalledFlag() {
	docker := maldocker.NewDockerClient()
	for i, plugin := range Plugs.Plugins {
		if _, exists, _ := docker.ImageExists(plugin.Image); exists {
			Plugs.Plugins[i].Installed = true
		}
	}
}
<commit_msg>add to Plugin struct too<commit_after>package plugins

import (
	"io/ioutil"
	"os"
	"path"

	"github.com/BurntSushi/toml"
	log "github.com/Sirupsen/logrus"
	er "github.com/maliceio/malice/malice/errors"
	"github.com/maliceio/malice/malice/maldirs"
	"github.com/maliceio/malice/malice/maldocker"
)

// Plugin represents a single plugin setting.
type Plugin struct {
	Name        string   `toml:"name"`
	Enabled     bool     `toml:"enabled"`
	Category    string   `toml:"category"`
	Description string   `toml:"description"`
	Image       string   `toml:"image"`
	Repository  string   `toml:"repository"`
	APIKey      string   `toml:"apikey"`
	Mime        string   `toml:"mime"`
	HashTypes   []string `toml:"hashtypes"`
	Cmd         string   `toml:"cmd"`
	Env         []string `toml:"env"`
	Installed   bool
}

// Configuration represents the malice runtime plugins.
type Configuration struct {
	Plugins []Plugin `toml:"plugin"`
}

// Plugs represents the Malice runtime configuration
var Plugs Configuration

// Load plugins.toml into Plug var
func Load() {
	// Try to load plugins from
	// - git repo folder      : MALICE_ROOT/plugins/plugins.toml
	// - .malice folder       : $HOME/.malice/plugins.toml
	// - binary embedded file : bindata

	// Check for plugins config in repo
	if _, err := os.Stat("./plugins/plugins.toml"); err == nil {
		log.Debug("Malice plugins loaded from ./plugins/plugins.toml")

		_, err := toml.DecodeFile("./plugins/plugins.toml", &Plugs)
		er.CheckError(err)

		return
	}
	// Check for plugins config in .malice folder
	if _, err := os.Stat(path.Join(maldirs.GetBaseDir(), "./plugins.toml")); err == nil {
		homeConfigDir := path.Join(maldirs.GetBaseDir(), "./plugins.toml")
		log.Debug("Malice plugins loaded from ", homeConfigDir)

		_, err := toml.DecodeFile(homeConfigDir, &Plugs)
		er.CheckError(err)

		return
	}
	// Read plugin config out of bindata
	tomlData, err := Asset("plugins/plugins.toml")
	if err != nil {
		log.Error(err)
	}

	if _, err := toml.Decode(string(tomlData), &Plugs); err == nil {
		log.Debug("Malice plugins loaded from plugins/bindata.go")
		configPath := path.Join(maldirs.GetBaseDir(), "./plugins.toml")
		er.CheckError(ioutil.WriteFile(configPath, tomlData, 0644))
	}
	er.CheckError(err)

	return
}

func setInstalledFlag() {
	docker := maldocker.NewDockerClient()
	for i, plugin := range Plugs.Plugins {
		if _, exists, _ := docker.ImageExists(plugin.Image); exists {
			Plugs.Plugins[i].Installed = true
		}
	}
}
<|endoftext|>


<commit_before>package model

import (
	"errors"
	"regexp"
)

type DeviceType string

const (
	IOS     DeviceType = "IOS"
	ANDROID DeviceType = "ANDROID"
	KINDLE  DeviceType = "KINDLE"
)

//TODO: constants for locale and timezone
type Device struct {
	id         string
	userAlias  string
	appId      string
	deviceType DeviceType
	arn        string
	token      string
	locale     string
	timezone   string
	enabled    bool
}

func NewDevice(id string, userAlias string, appId string, dt DeviceType, arn string, token string, locale string, timezone string, enabled bool) (*Device, error) {
	device := &Device{id: id, userAlias: userAlias, appId: appId, arn: arn, token: token, enabled: enabled}
	if err := device.SetDeviceType(dt); err != nil {
		return nil, err
	}
	if err := device.SetLocale(locale); err != nil {
		return nil, err
	}
	//TODO validate
	device.SetTimezone(timezone)
	return device, nil
}

func (device Device) Id() string {
	return device.id
}

func (device Device) UserAlias() string {
	return device.userAlias
}

func (device Device) AppId() string {
	return device.appId
}

func (device Device) Locale() string {
	return device.locale
}

func (device Device) DeviceType() DeviceType {
	return device.deviceType
}

func (device Device) Arn() string {
	return device.arn
}

func (device Device) Token() string {
	return device.token
}

func (device Device) Enabled() bool {
	return device.enabled
}

func (device Device) Timezone() string {
	return device.timezone
}

func (device Device) Platform() Platform {
	if device.deviceType == IOS {
		return APNS
	} else if device.deviceType == ANDROID {
		return GCM
	} else if device.deviceType == KINDLE {
		return ADM
	}
	return UNKNOWN
}

func (device *Device) SetId(id string) {
	device.id = id
}

func (device *Device) SetUserAlias(id string) {
	device.userAlias = id
}

func (device *Device) SetAppId(id string) {
	device.appId = id
}

func (device *Device) SetLocale(locale string) error {
	if err := ValidateLocale(locale); err != nil {
		return err
	}
	device.locale = locale
	return nil
}

func (device *Device) SetTimezone(timezone string) {
	device.timezone = timezone
}

func (device *Device) SetDeviceType(dt DeviceType) error {
	if err := ValidateDeviceType(dt); err != nil {
		return err
	}
	device.deviceType = dt
	return nil
}

func (device *Device) SetArn(arn string) {
	device.arn = arn
}

func (device *Device) SetToken(token string) {
	device.token = token
}

func (device *Device) SetEnabled(enabled bool) {
	device.enabled = enabled
}

var localRegex = regexp.MustCompile(`^[A-Za-z]{2,3}_[A-Za-z]{2,3}$`)

func ValidateLocale(locale string) error {
	if !localRegex.MatchString(locale) {
		return errors.New("Invalid locale, should be of the form US_en.")
	}

	return nil
}

var deviceTypeRegex = regexp.MustCompile("^(" + string(ANDROID) + "|" + string(IOS) + "|" + string(KINDLE) + ")$")

func ValidateDeviceType(dt DeviceType) error {
	if !deviceTypeRegex.MatchString(string(dt)) {
		return errors.New("Invalid device type, valid values: " + string(ANDROID) + "," + string(IOS) + " or " + string(KINDLE))
	}

	return nil
}
<commit_msg>add platformId to device<commit_after>package model

import (
	"errors"
	"regexp"
)

type DeviceType string

const (
	IOS     DeviceType = "IOS"
	ANDROID DeviceType = "ANDROID"
	KINDLE  DeviceType = "KINDLE"
)

//TODO: constants for locale and timezone
type Device struct {
	id         		string
	userAlias  		string
	appId      		string
	platformAppId 	string
	deviceType 		DeviceType
	arn        		string
	token      		string
	locale     		string
	timezone   		string
	enabled    		bool
}

func NewDevice(id string, userAlias string, appId string, pappId string, dt DeviceType, arn string, token string, locale string, timezone string, enabled bool) (*Device, error) {
	device := &Device{id: id, userAlias: userAlias, appId: appId, platformAppId: pappId, arn: arn, token: token, enabled: enabled}
	if err := device.SetDeviceType(dt); err != nil {
		return nil, err
	}
	if err := device.SetLocale(locale); err != nil {
		return nil, err
	}
	//TODO validate
	device.SetTimezone(timezone)
	return device, nil
}

func (device Device) Id() string {
	return device.id
}

func (device Device) UserAlias() string {
	return device.userAlias
}

func (device Device) AppId() string {
	return device.appId
}

func (device Device) PlatformAppId() string {
	return device.platformAppId
}

func (device Device) Locale() string {
	return device.locale
}

func (device Device) DeviceType() DeviceType {
	return device.deviceType
}

func (device Device) Arn() string {
	return device.arn
}

func (device Device) Token() string {
	return device.token
}

func (device Device) Enabled() bool {
	return device.enabled
}

func (device Device) Timezone() string {
	return device.timezone
}

func (device Device) Platform() Platform {
	if device.deviceType == IOS {
		return APNS
	} else if device.deviceType == ANDROID {
		return GCM
	} else if device.deviceType == KINDLE {
		return ADM
	}
	return UNKNOWN
}

func (device *Device) SetId(id string) {
	device.id = id
}

func (device *Device) SetUserAlias(id string) {
	device.userAlias = id
}

func (device *Device) SetAppId(id string) {
	device.appId = id
}

func (device *Device) SetPlatformAppId(papp string) {
	device.platformAppId = papp
}

func (device *Device) SetLocale(locale string) error {
	if err := ValidateLocale(locale); err != nil {
		return err
	}
	device.locale = locale
	return nil
}

func (device *Device) SetTimezone(timezone string) {
	device.timezone = timezone
}

func (device *Device) SetDeviceType(dt DeviceType) error {
	if err := ValidateDeviceType(dt); err != nil {
		return err
	}
	device.deviceType = dt
	return nil
}

func (device *Device) SetArn(arn string) {
	device.arn = arn
}

func (device *Device) SetToken(token string) {
	device.token = token
}

func (device *Device) SetEnabled(enabled bool) {
	device.enabled = enabled
}

var localRegex = regexp.MustCompile(`^[A-Za-z]{2,3}_[A-Za-z]{2,3}$`)

func ValidateLocale(locale string) error {
	if !localRegex.MatchString(locale) {
		return errors.New("Invalid locale, should be of the form US_en.")
	}

	return nil
}

var deviceTypeRegex = regexp.MustCompile("^(" + string(ANDROID) + "|" + string(IOS) + "|" + string(KINDLE) + ")$")

func ValidateDeviceType(dt DeviceType) error {
	if !deviceTypeRegex.MatchString(string(dt)) {
		return errors.New("Invalid device type, valid values: " + string(ANDROID) + "," + string(IOS) + " or " + string(KINDLE))
	}

	return nil
}
<|endoftext|>


<commit_before>package cache

import (
	"encoding/json"
	"gost/config"
	"testing"
	"time"
)

type CacheTest struct {
	X int
	Y int
	Z int
}

func TestCache(t *testing.T) {
	const cacheExpireTime = 1 * time.Second

	var queries = []string{
		"test:x%2==0",
		"test:(x+y)%z>1",
		"test:z>550",
		"thisNeedsToExpire",
	}

	var items []CacheTest
	var cachedItems []*Cache
	var expiringItem *Cache

	config.InitTestsDatabase()
	StartCachingSystem(cacheExpireTime)
	defer StopCachingSystem()

	items = testInitItems(t)

	testFetchInexistentCache(t, queries[0])
	cachedItems, expiringItem = testAddingToCache(t, items, cacheExpireTime, queries)
	testFetchingFromCache(t, cachedItems)
	testRemovingFromCache(t, cachedItems)
	testFetchInexistentCache(t, queries[1])

	time.Sleep(2 * cacheExpireTime)
	testExpiringItem(t, expiringItem)
}

func testExpiringItem(t *testing.T, expiringItem *Cache) {
	it := QueryCache(expiringItem.Query)

	if it != nil {
		t.Fatal("The cache items did not properly expire")
	}
}

func testFetchInexistentCache(t *testing.T, mockQuery string) {
	// Will never be added
	data := QueryCache("keySFAFSAGKAGHAJSKfhaskfhaskf")
	if data != nil {
		t.Fatal("Unexpected output from cache")
	}

	// Will be added later during the test
	data = QueryCache(mockQuery)
	if data != nil {
		t.Fatal("Unexpected output from cache")
	}
}

func testFetchingFromCache(t *testing.T, cachedItems []*Cache) {
	var q1 *Cache
	var q2 *Cache
	var q3 *Cache
	i := 0

	for i < 2 {
		q1 = QueryCache(cachedItems[0].Query)
		q2 = QueryCache(cachedItems[1].Query)
		q3 = QueryCache(cachedItems[2].Query)

		if q1 == nil || q2 == nil || q3 == nil {
			t.Fatal("Cache didn't properly return test items")
		}

		i++
	}

	if q1.Query != cachedItems[0].Query || q2.Query != cachedItems[1].Query || q3.Query != cachedItems[2].Query {
		t.Fatal("Wrong cache values were returned")
	}
}

func testAddingToCache(t *testing.T, items []CacheTest, cacheExpireTime time.Duration, queries []string) ([]*Cache, *Cache) {
	var cachedItems []*Cache
	var expiringCacheItem *Cache

	q1 := make([]CacheTest, 0)
	q2 := make([]CacheTest, 0)
	q3 := make([]CacheTest, 0)

	expireTime := time.Now().Add(cacheExpireTime)

	// First type
	for i := 0; i < len(items); i++ {
		if items[i].X%2 == 0 {
			q1 = append(q1, items[i])
		}
	}
	j1, _ := json.MarshalIndent(q1, "", "  ")
	c1 := &Cache{
		Query:      queries[0],
		Data:       j1,
		ExpireTime: expireTime,
	}
	c1.Cache()
	cachedItems = append(cachedItems, c1)

	// Second type
	for i := 0; i < len(items); i++ {
		if ((items[i].X + items[i].Y) % items[i].Z) > 1 {
			q2 = append(q2, items[i])
		}
	}
	j2, _ := json.MarshalIndent(q2, "", "  ")
	c2 := &Cache{
		Query:      queries[1],
		Data:       j2,
		ExpireTime: expireTime,
	}
	c2.Cache()
	cachedItems = append(cachedItems, c2)

	// Third type
	for i := 0; i < len(items); i++ {
		if items[i].Z > 550 {
			q3 = append(q3, items[i])
		}
	}
	j3, _ := json.MarshalIndent(q3, "", "  ")
	c3 := &Cache{
		Query:      queries[2],
		Data:       j3,
		ExpireTime: expireTime,
	}
	c3.Cache()
	cachedItems = append(cachedItems, c3)

	// Expiring type
	expiringCacheItem = &Cache{
		Query:      queries[3],
		Data:       j1,
		ExpireTime: expireTime,
	}
	expiringCacheItem.Cache()

	return cachedItems, expiringCacheItem
}

func testRemovingFromCache(t *testing.T, cachedItems []*Cache) {
	for _, it := range cachedItems {
		it.Invalidate()
	}
}

func testInitItems(t *testing.T) []CacheTest {
	var items []CacheTest

	for i := 1; i < 1000; i++ {
		items = append(items, CacheTest{
			X: i,
			Y: i * 11 / 3,
			Z: i * 3,
		})
	}

	return items
}
<commit_msg>Added some verbosity to the cache tests and fixed a bug that prevented the cache package from compiling<commit_after>package cache

import (
	"encoding/json"
	"gost/config"
	"log"
	"testing"
	"time"
)

type CacheTest struct {
	X int
	Y int
	Z int
}

func TestCache(t *testing.T) {
	const cacheExpireTime = 1 * time.Second

	var queries = []string{
		"test:x%2==0",
		"test:(x+y)%z>1",
		"test:z>550",
		"thisNeedsToExpire",
	}

	var items []CacheTest
	var cachedItems []*Cache
	var expiringItem *Cache

	config.InitTestsDatabase()
	StartCachingSystem(cacheExpireTime)
	defer StopCachingSystem()

	items = testInitItems(t)

	testFetchInexistentCache(t, queries[0])
	cachedItems, expiringItem = testAddingToCache(t, items, cacheExpireTime, queries)
	testFetchingFromCache(t, cachedItems)
	testRemovingFromCache(t, cachedItems)
	testFetchInexistentCache(t, queries[1])
	testExpiringItem(t, expiringItem, cacheExpireTime)
}

func testExpiringItem(t *testing.T, expiringItem *Cache, cacheExpireTime time.Duration) {
	log.Println("Testing the expired cache invalidation system")

	time.Sleep(2 * cacheExpireTime)

	it := QueryCache(expiringItem.Query)

	if it != nil {
		t.Fatal("The cache items did not properly expire")
	}
}

func testFetchInexistentCache(t *testing.T, mockQuery string) {
	log.Println("Testing the cache querying system with inexistent or invalid data")

	// Will never be added
	data := QueryCache("keySFAFSAGKAGHAJSKfhaskfhaskf")
	if data != nil {
		t.Fatal("Unexpected output from cache")
	}

	// Will be added later during the test
	data = QueryCache(mockQuery)
	if data != nil {
		t.Fatal("Unexpected output from cache")
	}
}

func testFetchingFromCache(t *testing.T, cachedItems []*Cache) {
	log.Println("Testing the cache querying system with valid data")

	var q1 *Cache
	var q2 *Cache
	var q3 *Cache
	i := 0

	for i < 2 {
		q1 = QueryCache(cachedItems[0].Query)
		q2 = QueryCache(cachedItems[1].Query)
		q3 = QueryCache(cachedItems[2].Query)

		if q1 == nil || q2 == nil || q3 == nil {
			t.Fatal("Cache didn't properly return test items")
		}

		i++
	}

	if q1.Query != cachedItems[0].Query || q2.Query != cachedItems[1].Query || q3.Query != cachedItems[2].Query {
		t.Fatal("Wrong cache values were returned")
	}
}

func testAddingToCache(t *testing.T, items []CacheTest, cacheExpireTime time.Duration, queries []string) ([]*Cache, *Cache) {
	log.Println("Testing the data caching system")

	var cachedItems []*Cache
	var expiringCacheItem *Cache

	q1 := make([]CacheTest, 0)
	q2 := make([]CacheTest, 0)
	q3 := make([]CacheTest, 0)

	expireTime := time.Now().Add(cacheExpireTime)

	// First type
	for i := 0; i < len(items); i++ {
		if items[i].X%2 == 0 {
			q1 = append(q1, items[i])
		}
	}
	j1, _ := json.MarshalIndent(q1, "", "  ")
	c1 := &Cache{
		Query:      queries[0],
		Data:       j1,
		ExpireTime: expireTime,
	}
	c1.Cache()
	cachedItems = append(cachedItems, c1)

	// Second type
	for i := 0; i < len(items); i++ {
		if ((items[i].X + items[i].Y) % items[i].Z) > 1 {
			q2 = append(q2, items[i])
		}
	}
	j2, _ := json.MarshalIndent(q2, "", "  ")
	c2 := &Cache{
		Query:      queries[1],
		Data:       j2,
		ExpireTime: expireTime,
	}
	c2.Cache()
	cachedItems = append(cachedItems, c2)

	// Third type
	for i := 0; i < len(items); i++ {
		if items[i].Z > 550 {
			q3 = append(q3, items[i])
		}
	}
	j3, _ := json.MarshalIndent(q3, "", "  ")
	c3 := &Cache{
		Query:      queries[2],
		Data:       j3,
		ExpireTime: expireTime,
	}
	c3.Cache()
	cachedItems = append(cachedItems, c3)

	// Expiring type
	expiringCacheItem = &Cache{
		Query:      queries[3],
		Data:       j1,
		ExpireTime: expireTime,
	}
	expiringCacheItem.Cache()

	return cachedItems, expiringCacheItem
}

func testRemovingFromCache(t *testing.T, cachedItems []*Cache) {
	log.Println("Testing the cache invalidation system")

	for _, it := range cachedItems {
		it.Invalidate()
	}
}

func testInitItems(t *testing.T) []CacheTest {
	var items []CacheTest

	for i := 1; i < 1000; i++ {
		items = append(items, CacheTest{
			X: i,
			Y: i * 11 / 3,
			Z: i * 3,
		})
	}

	return items
}
<|endoftext|>


<commit_before>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

import (
	"github.com/bboozzoo/q3stats/store"
	"github.com/jinzhu/gorm"
)

const (
	NoUser = 0
)

// Alias is a nickname used during the match.
type Alias struct {
	gorm.Model

	// alias, ex. 'honeybunny', 'Klesk'
	Alias string

	// player claiming this alias
	PlayerID uint
}

func GetAliases(store store.DB, user int) []Alias {
	var aliases []Alias
	store.Conn().
		Where(&Alias{PlayerID: 0}).
		Find(&aliases)

	return aliases
}
<commit_msg>models/alias: stub for claiming player aliases<commit_after>// The MIT License (MIT)

// Copyright (c) 2016 Maciej Borzecki

// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package models

import (
	"github.com/bboozzoo/q3stats/store"
	"github.com/jinzhu/gorm"
)

const (
	NoUser = 0
)

// Alias is a nickname used during the match.
type Alias struct {
	gorm.Model

	// alias, ex. 'honeybunny', 'Klesk'
	Alias string

	// player claiming this alias
	PlayerID uint
}

func GetAliases(store store.DB, user int) []Alias {
	var aliases []Alias
	store.Conn().
		Where(&Alias{PlayerID: 0}).
		Find(&aliases)

	return aliases
}

func ClaimAliasesByPlayer(store store.DB, player uint, aliases []string) {
	// update aliases set player_id = `player` where aliases in
	// `aliases`?
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package models

import (
	"encoding/json"
	"errors"
	"time"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/r3labs/graph"
	"github.com/sirupsen/logrus"
)

// Build : holds the build response from service store
type Build struct {
	ID            string                 `json:"id"`
	EnvironmentID int                    `json:"environment_id"`
	UserID        int                    `json:"user_id"`
	Username      string                 `json:"user_name"`
	Type          string                 `json:"type"`
	Status        string                 `json:"status"`
	Definition    string                 `json:"definition"`
	Mapping       map[string]interface{} `json:"mapping"`
	Validation    *BuildValidateResponse `json:"validation,omitempty"`
	CreatedAt     time.Time              `json:"created_at"`
	UpdatedAt     time.Time              `json:"updated_at"`
}

// Validate the env
func (b *Build) Validate() error {
	if b.EnvironmentID == 0 {
		return errors.New("Build environment id is empty")
	}

	if b.Type == "" {
		return errors.New("Build type is empty")
	}

	return nil
}

// Map : maps a build from a request's body and validates the input
func (b *Build) Map(data []byte) error {
	if err := json.Unmarshal(data, b); err != nil {
		h.L.WithFields(logrus.Fields{
			"input": string(data),
		}).Error("Couldn't unmarshal given input")
		return NewError(InvalidInputCode, "Invalid input")
	}

	if err := b.Validate(); err != nil {
		h.L.WithFields(logrus.Fields{
			"input": string(data),
		}).Warning("Input is not valid")
		return NewError(InvalidInputCode, "Invalid input")
	}

	return nil
}

// Find : Searches for all builds with filters
func (b *Build) Find(query map[string]interface{}, builds *[]Build) (err error) {
	return NewBaseModel(b.getStore()).FindBy(query, builds)
}

// FindLastByName : Searches for all environments with a name equal to the specified
func (b *Build) FindLastByName(env string) (err error) {
	var e Env

	err = e.FindByName(env)
	if err != nil {
		return
	}

	query := make(map[string]interface{})
	query["environment_id"] = e.ID
	return NewBaseModel(b.getStore()).GetBy(query, b)
}

// FindByName : Searches for all builds with by env name
func (b *Build) FindByName(env string) (builds []Build, err error) {
	var e Env

	err = e.FindByName(env)
	if err != nil {
		return
	}

	query := make(map[string]interface{})
	query["environment_id"] = e.ID

	return builds, NewBaseModel(b.getStore()).FindBy(query, &builds)
}

// FindByID : Gets a model by its id
func (b *Build) FindByID(id string) (err error) {
	query := make(map[string]interface{})
	query["id"] = id
	return NewBaseModel(b.getStore()).GetBy(query, b)
}

// FindAll : Searches for all builds on the store current user
// has access to
func (b *Build) FindAll(au User, builds *[]Build) (err error) {
	query := make(map[string]interface{})
	return NewBaseModel(b.getStore()).FindBy(query, builds)
}

// Save : calls build.set with the marshalled
func (b *Build) Save() (err error) {
	return NewBaseModel(b.getStore()).Save(b)
}

// Delete : will delete a builds by its id
func (b *Build) Delete() (err error) {
	query := make(map[string]interface{})
	query["id"] = b.ID
	return NewBaseModel(b.getStore()).Delete(query)
}

// GetMapping : will get a builds mapping
func (b *Build) GetMapping() (*graph.Graph, error) {
	var m Mapping
	query := make(map[string]interface{})
	query["id"] = b.ID

	err := NewBaseModel(b.getStore()).CallStoreBy("get.mapping", query, &m)
	if err != nil {
		return nil, err
	}

	g := graph.New()
	err = g.Load(m)

	return g, err
}

// GetRawMapping : will get a builds mapping
func (b *Build) GetRawMapping() (map[string]interface{}, error) {
	var m Mapping
	query := make(map[string]interface{})
	query["id"] = b.ID

	err := NewBaseModel(b.getStore()).CallStoreBy("get.mapping", query, &m)
	if err != nil {
		return nil, err
	}

	return m, err
}

// GetDefinition : will get a builds mapping
func (b *Build) GetDefinition() ([]byte, error) {
	var def []byte
	query := make(map[string]interface{})
	query["id"] = b.ID

	return def, NewBaseModel(b.getStore()).CallStoreByRaw("get.definition", query, &def)
}

// Reset : will reset the builds status to errored
func (b *Build) Reset() error {
	var r map[string]interface{}

	b.Status = "errored"
	query := make(map[string]interface{})
	query["id"] = b.ID
	query["status"] = "errored"

	data, err := json.Marshal(query)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}

	resp, err := N.Request("build.set.status", data, time.Second*5)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}

	err = json.Unmarshal(resp.Data, &r)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}

	if r["error"] != nil {
		err = errors.New(r["error"].(string))
		h.L.Error(err.Error())
		return err
	}

	return nil
}

// FindByEnvironmentName : find a builds for the given environment name
func (b *Build) FindByEnvironmentName(name string, builds *[]Build) (err error) {
	var e Env

	err = e.FindByName(name)
	if err != nil {
		return err
	}

	query := make(map[string]interface{})
	query["environment_id"] = e.ID

	return NewBaseModel(b.getStore()).FindBy(query, builds)
}

// RequestCreation : calls env.create with the given raw message
func (b *Build) RequestCreation(mapping *Mapping) error {
	data, err := json.Marshal(mapping)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}
	if err := N.Publish(b.getStore()+".create", data); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

// RequestImport : calls build.import with the given raw message
func (b *Build) RequestImport(mapping *Mapping) error {
	data, err := json.Marshal(mapping)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}
	if err := N.Publish(b.getStore()+".import", data); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

// RequestDeletion : calls builddelete with the given raw message
func (b *Build) RequestDeletion(mapping *Mapping) error {
	data, err := json.Marshal(mapping)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}
	if err := N.Publish(b.getStore()+".delete", data); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

/*

// RequestSync : calls service.sync with the given raw message
func (b *Build) RequestSync() error {
	if err := N.Publish(s.getStore()+".sync", []byte(`{"id":"`+s.ID+`"}`)); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

*/

// GetID : ID getter
func (b *Build) GetID() string {
	return b.ID
}

// GetType : Gets the resource type
func (b *Build) GetType() string {
	return "build"
}

// getStore : Gets the store name
func (b *Build) getStore() string {
	return "build"
}
<commit_msg>mapping build errors<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package models

import (
	"encoding/json"
	"errors"
	"time"

	h "github.com/ernestio/api-gateway/helpers"
	"github.com/r3labs/graph"
	"github.com/sirupsen/logrus"
)

// Build : holds the build response from service store
type Build struct {
	ID            string                 `json:"id"`
	EnvironmentID int                    `json:"environment_id"`
	UserID        int                    `json:"user_id"`
	Username      string                 `json:"user_name"`
	Type          string                 `json:"type"`
	Status        string                 `json:"status"`
	Definition    string                 `json:"definition"`
	Mapping       map[string]interface{} `json:"mapping"`
	Validation    *BuildValidateResponse `json:"validation,omitempty"`
	Errors        []string               `json:"errors,omitempty"`
	CreatedAt     time.Time              `json:"created_at"`
	UpdatedAt     time.Time              `json:"updated_at"`
}

// Validate the env
func (b *Build) Validate() error {
	if b.EnvironmentID == 0 {
		return errors.New("Build environment id is empty")
	}

	if b.Type == "" {
		return errors.New("Build type is empty")
	}

	return nil
}

// Map : maps a build from a request's body and validates the input
func (b *Build) Map(data []byte) error {
	if err := json.Unmarshal(data, b); err != nil {
		h.L.WithFields(logrus.Fields{
			"input": string(data),
		}).Error("Couldn't unmarshal given input")
		return NewError(InvalidInputCode, "Invalid input")
	}

	if err := b.Validate(); err != nil {
		h.L.WithFields(logrus.Fields{
			"input": string(data),
		}).Warning("Input is not valid")
		return NewError(InvalidInputCode, "Invalid input")
	}

	return nil
}

// Find : Searches for all builds with filters
func (b *Build) Find(query map[string]interface{}, builds *[]Build) (err error) {
	return NewBaseModel(b.getStore()).FindBy(query, builds)
}

// FindLastByName : Searches for all environments with a name equal to the specified
func (b *Build) FindLastByName(env string) (err error) {
	var e Env

	err = e.FindByName(env)
	if err != nil {
		return
	}

	query := make(map[string]interface{})
	query["environment_id"] = e.ID
	return NewBaseModel(b.getStore()).GetBy(query, b)
}

// FindByName : Searches for all builds with by env name
func (b *Build) FindByName(env string) (builds []Build, err error) {
	var e Env

	err = e.FindByName(env)
	if err != nil {
		return
	}

	query := make(map[string]interface{})
	query["environment_id"] = e.ID

	return builds, NewBaseModel(b.getStore()).FindBy(query, &builds)
}

// FindByID : Gets a model by its id
func (b *Build) FindByID(id string) (err error) {
	query := make(map[string]interface{})
	query["id"] = id

	err = NewBaseModel(b.getStore()).GetBy(query, b)
	if err != nil {
		return err
	}

	if b.Status != "errored" {
		return nil
	}

	return b.LoadMappingErrors()
}

// FindAll : Searches for all builds on the store current user
// has access to
func (b *Build) FindAll(au User, builds *[]Build) (err error) {
	query := make(map[string]interface{})
	return NewBaseModel(b.getStore()).FindBy(query, builds)
}

// Save : calls build.set with the marshalled
func (b *Build) Save() (err error) {
	return NewBaseModel(b.getStore()).Save(b)
}

// Delete : will delete a builds by its id
func (b *Build) Delete() (err error) {
	query := make(map[string]interface{})
	query["id"] = b.ID
	return NewBaseModel(b.getStore()).Delete(query)
}

// GetMapping : will get a builds mapping
func (b *Build) GetMapping() (*graph.Graph, error) {
	var m Mapping
	query := make(map[string]interface{})
	query["id"] = b.ID

	err := NewBaseModel(b.getStore()).CallStoreBy("get.mapping", query, &m)
	if err != nil {
		return nil, err
	}

	g := graph.New()
	err = g.Load(m)

	return g, err
}

// GetRawMapping : will get a builds mapping
func (b *Build) GetRawMapping() (map[string]interface{}, error) {
	var m Mapping
	query := make(map[string]interface{})
	query["id"] = b.ID

	err := NewBaseModel(b.getStore()).CallStoreBy("get.mapping", query, &m)
	if err != nil {
		return nil, err
	}

	return m, err
}

// GetDefinition : will get a builds mapping
func (b *Build) GetDefinition() ([]byte, error) {
	var def []byte
	query := make(map[string]interface{})
	query["id"] = b.ID

	return def, NewBaseModel(b.getStore()).CallStoreByRaw("get.definition", query, &def)
}

// Reset : will reset the builds status to errored
func (b *Build) Reset() error {
	var r map[string]interface{}

	b.Status = "errored"
	query := make(map[string]interface{})
	query["id"] = b.ID
	query["status"] = "errored"

	data, err := json.Marshal(query)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}

	resp, err := N.Request("build.set.status", data, time.Second*5)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}

	err = json.Unmarshal(resp.Data, &r)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}

	if r["error"] != nil {
		err = errors.New(r["error"].(string))
		h.L.Error(err.Error())
		return err
	}

	return nil
}

// FindByEnvironmentName : find a builds for the given environment name
func (b *Build) FindByEnvironmentName(name string, builds *[]Build) (err error) {
	var e Env

	err = e.FindByName(name)
	if err != nil {
		return err
	}

	query := make(map[string]interface{})
	query["environment_id"] = e.ID

	return NewBaseModel(b.getStore()).FindBy(query, builds)
}

// RequestCreation : calls env.create with the given raw message
func (b *Build) RequestCreation(mapping *Mapping) error {
	data, err := json.Marshal(mapping)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}
	if err := N.Publish(b.getStore()+".create", data); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

// RequestImport : calls build.import with the given raw message
func (b *Build) RequestImport(mapping *Mapping) error {
	data, err := json.Marshal(mapping)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}
	if err := N.Publish(b.getStore()+".import", data); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

// RequestDeletion : calls builddelete with the given raw message
func (b *Build) RequestDeletion(mapping *Mapping) error {
	data, err := json.Marshal(mapping)
	if err != nil {
		h.L.Error(err.Error())
		return err
	}
	if err := N.Publish(b.getStore()+".delete", data); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

// LoadMappingErrors : loads all errors from a build and maps them to the build model
func (b *Build) LoadMappingErrors() error {
	mapping, err := b.GetMapping()
	if err != nil {
		return err
	}

	for _, change := range mapping.Changes {
		c := change.(*graph.GenericComponent)
		ce, ok := (*c)["_error"].(string)
		if ok {
			b.Errors = append(b.Errors, ce)
		}
	}

	return nil
}

/*

// RequestSync : calls service.sync with the given raw message
func (b *Build) RequestSync() error {
	if err := N.Publish(s.getStore()+".sync", []byte(`{"id":"`+s.ID+`"}`)); err != nil {
		h.L.Error(err.Error())
		return err
	}
	return nil
}

*/

// GetID : ID getter
func (b *Build) GetID() string {
	return b.ID
}

// GetType : Gets the resource type
func (b *Build) GetType() string {
	return "build"
}

// getStore : Gets the store name
func (b *Build) getStore() string {
	return "build"
}
<|endoftext|>


<commit_before>package treasure

import (
	"bytes"
	"fmt"
)

// Kode for Oppgave 3c
// Bruk strengen fra filen treasure.txt som in-data for denne funksjonen
const treasureString = "\x48\x65\x6e\x72\x69\x6b\x20\x41\x72\x6e\x6f\x6c\x64\x20\x57\x65" +
	"\x72\x67\x65\x6c\x61\x6e\x64\x20\x28\x66\xf8\x64\x74\x20\x31\x37\x2e\x20\x6a" +
	"\x75\x6e\x69\x20\x31\x38\x30\x38\x2c\x20\x64\xf8\x64\x20\x31\x32\x2e\x20\x6a" +
	"\x75\x6c\x69\x20\x31\x38\x34\x35\x29\x0a\x56\x69\x20\x65\x72\x65\x20\x65\x6e" +
	"\x20\x6e\x61\x73\x6a\x6f\x6e\x20\x76\x69\x20\x6d\x65\x64\x2c\x0a\x20\x76\x69" +
	"\x20\x73\x6d\xe5\x20\x65\x6e\x20\x61\x6c\x65\x6e\x20\x6c\x61\x6e\x67\x65" +
	"\x2c\x0a\x20\x65\x74\x20\x66\x65\x64\x72\x65\x6c\x61\x6e\x64\x20\x76\x69\x20" +
	"\x66\x72\x79\x64\x65\x73\x20\x76\x65\x64\x2c\x0a\x20\x6f\x67\x20\x76\x69\x2c" +
	"\x20\x76\x69\x20\x65\x72\x65\x20\x6d\x61\x6e\x67\x65\x2e\x0a\x20\x56\xe5\x72" +
	"\x74\x20\x68\x6a\x65\x72\x74\x65\x20\x76\x65\x74\x2c\x20\x76\xe5\x72\x74\x20" +
	"\xf8\x79\x65\x20\x73\x65\x72\x0a\x20\x68\x76\x6f\x72\x20\x67\x6f\x64\x74\x20\x6f" +
	"\x67\x20\x76\x61\x6b\x6b\x65\x72\x74\x20\x4e\x6f\x72\x67\x65\x20\x65\x72\x2c" +
	"\x0a\x20\x76\xe5\x72\x20\x74\x75\x6e\x67\x65\x20\x6b\x61\x6e\x20\x65\x6e\x20" +
	"\x73\x61\x6e\x67\x20\x62\x6c\x61\x6e\x74\x20\x66\x6c\x65\x72\x0a\x20\x61\x76\x20" +
	"\x4e\x6f\x72\x67\x65\x73\x20\xe6\x72\x65\x73\x2d\x73\x61\x6e\x67\x65\x2e\x0a" +
	"\x0a\x20\x4d\x65\x72\x20\x67\x72\xf8\x6e\x74\x20\x65\x72\x20\x67\x72\x65\x73" +
	"\x73\x65\x74\x20\x69\x6e\x67\x65\x6e\x73\x74\x65\x64\x73\x2c\x0a\x20\x6d\x65" +
	"\x72\x20\x66\x75\x6c\x6c\x74\x20\x61\x76\x20\x62\x6c\x6f\x6d\x73\x74\x65\x72" +
	"\x20\x76\x65\x76\x65\x74\x0a\x20\x65\x6e\x6e\x20\x69\x20\x64\x65\x74\x20\x6c" +
	"\x61\x6e\x64\x20\x68\x76\x6f\x72\x20\x6a\x65\x67\x20\x74\x69\x6c\x66\x72\x65" +
	"\x64\x73\x0a\x20\x6d\x65\x64\x20\x66\x61\x72\x20\x6f\x67\x20\x6d\x6f\x72\x20" +
	"\x68\x61\x72\x20\x6c\x65\x76\x65\x74\x2e\x0a\x20\x4a\x65\x67\x20\x76\x69\x6c" +
	"\x20\x64\x65\x74\x20\x65\x6c\x73\x6b\x65\x20\x74\x69\x6c\x20\x6d\x69\x6e\x20" +
	"\x64\xf8\x64\x2c\x0a\x20\x65\x69\x20\x62\x79\x74\x74\x65\x20\x64\x65\x74\x20" +
	"\x68\x76\x6f\x72\x20\x6a\x65\x67\x20\x65\x72\x20\x66\xf8\x64\x64\x2c\x0a\x20" +
	"\x6f\x6d\x20\x6d\x61\x6e\x20\x65\x74\x20\x70\x61\x72\x61\x64\x69\x73\x20\x6d" +
	"\x65\x67\x20\x62\xf8\x64\x0a\x20\x61\x76\x20\x70\x61\x6c\x6d\x65\x72\x20\x6f" +
	"\x76\x65\x72\x73\x76\x65\x76\x65\x74\x2e\x0a"

//PrintTreasureUT8. Vi valgte å bruke egen kode ettersom at det var lettere for oss å forstå
func Replace() {

	s := bytes.Replace([]byte(a), []byte("\xe5"), []byte("\xc3\xa5"), -1)
	e := bytes.Replace([]byte(s), []byte("\xc6"), []byte("\xc3\x86"), -1)
	r := bytes.Replace([]byte(e), []byte("\xf8"), []byte("\xc3\xb8"), -1)
	t := bytes.Replace([]byte(r), []byte("\xd8"), []byte("\xc3\x98"), -1)
	y := bytes.Replace([]byte(t), []byte("\xe6"), []byte("\xc3\xa6"), -1)
	fmt.Printf("%s\n", bytes.Replace([]byte(y), []byte("\xc6"), []byte("\xc3\x86"), -1))

}
<commit_msg>Update treasure.go<commit_after>package treasure

import (
	"bytes"
	"fmt"
)

// Kode for Oppgave 3c
// Bruk strengen fra filen treasure.txt som in-data for denne funksjonen
const treasureString = "\x48\x65\x6e\x72\x69\x6b\x20\x41\x72\x6e\x6f\x6c\x64\x20\x57\x65" +
	"\x72\x67\x65\x6c\x61\x6e\x64\x20\x28\x66\xf8\x64\x74\x20\x31\x37\x2e\x20\x6a" +
	"\x75\x6e\x69\x20\x31\x38\x30\x38\x2c\x20\x64\xf8\x64\x20\x31\x32\x2e\x20\x6a" +
	"\x75\x6c\x69\x20\x31\x38\x34\x35\x29\x0a\x56\x69\x20\x65\x72\x65\x20\x65\x6e" +
	"\x20\x6e\x61\x73\x6a\x6f\x6e\x20\x76\x69\x20\x6d\x65\x64\x2c\x0a\x20\x76\x69" +
	"\x20\x73\x6d\xe5\x20\x65\x6e\x20\x61\x6c\x65\x6e\x20\x6c\x61\x6e\x67\x65" +
	"\x2c\x0a\x20\x65\x74\x20\x66\x65\x64\x72\x65\x6c\x61\x6e\x64\x20\x76\x69\x20" +
	"\x66\x72\x79\x64\x65\x73\x20\x76\x65\x64\x2c\x0a\x20\x6f\x67\x20\x76\x69\x2c" +
	"\x20\x76\x69\x20\x65\x72\x65\x20\x6d\x61\x6e\x67\x65\x2e\x0a\x20\x56\xe5\x72" +
	"\x74\x20\x68\x6a\x65\x72\x74\x65\x20\x76\x65\x74\x2c\x20\x76\xe5\x72\x74\x20" +
	"\xf8\x79\x65\x20\x73\x65\x72\x0a\x20\x68\x76\x6f\x72\x20\x67\x6f\x64\x74\x20\x6f" +
	"\x67\x20\x76\x61\x6b\x6b\x65\x72\x74\x20\x4e\x6f\x72\x67\x65\x20\x65\x72\x2c" +
	"\x0a\x20\x76\xe5\x72\x20\x74\x75\x6e\x67\x65\x20\x6b\x61\x6e\x20\x65\x6e\x20" +
	"\x73\x61\x6e\x67\x20\x62\x6c\x61\x6e\x74\x20\x66\x6c\x65\x72\x0a\x20\x61\x76\x20" +
	"\x4e\x6f\x72\x67\x65\x73\x20\xe6\x72\x65\x73\x2d\x73\x61\x6e\x67\x65\x2e\x0a" +
	"\x0a\x20\x4d\x65\x72\x20\x67\x72\xf8\x6e\x74\x20\x65\x72\x20\x67\x72\x65\x73" +
	"\x73\x65\x74\x20\x69\x6e\x67\x65\x6e\x73\x74\x65\x64\x73\x2c\x0a\x20\x6d\x65" +
	"\x72\x20\x66\x75\x6c\x6c\x74\x20\x61\x76\x20\x62\x6c\x6f\x6d\x73\x74\x65\x72" +
	"\x20\x76\x65\x76\x65\x74\x0a\x20\x65\x6e\x6e\x20\x69\x20\x64\x65\x74\x20\x6c" +
	"\x61\x6e\x64\x20\x68\x76\x6f\x72\x20\x6a\x65\x67\x20\x74\x69\x6c\x66\x72\x65" +
	"\x64\x73\x0a\x20\x6d\x65\x64\x20\x66\x61\x72\x20\x6f\x67\x20\x6d\x6f\x72\x20" +
	"\x68\x61\x72\x20\x6c\x65\x76\x65\x74\x2e\x0a\x20\x4a\x65\x67\x20\x76\x69\x6c" +
	"\x20\x64\x65\x74\x20\x65\x6c\x73\x6b\x65\x20\x74\x69\x6c\x20\x6d\x69\x6e\x20" +
	"\x64\xf8\x64\x2c\x0a\x20\x65\x69\x20\x62\x79\x74\x74\x65\x20\x64\x65\x74\x20" +
	"\x68\x76\x6f\x72\x20\x6a\x65\x67\x20\x65\x72\x20\x66\xf8\x64\x64\x2c\x0a\x20" +
	"\x6f\x6d\x20\x6d\x61\x6e\x20\x65\x74\x20\x70\x61\x72\x61\x64\x69\x73\x20\x6d" +
	"\x65\x67\x20\x62\xf8\x64\x0a\x20\x61\x76\x20\x70\x61\x6c\x6d\x65\x72\x20\x6f" +
	"\x76\x65\x72\x73\x76\x65\x76\x65\x74\x2e\x0a"

//PrintTreasureUT8. Vi valgte å bruke egen kode ettersom at det var lettere for oss å forstå
func Replace() {

	s := bytes.Replace([]byte(treasureString), []byte("\xe5"), []byte("\xc3\xa5"), -1)
	e := bytes.Replace([]byte(s), []byte("\xc6"), []byte("\xc3\x86"), -1)
	r := bytes.Replace([]byte(e), []byte("\xf8"), []byte("\xc3\xb8"), -1)
	t := bytes.Replace([]byte(r), []byte("\xd8"), []byte("\xc3\x98"), -1)
	y := bytes.Replace([]byte(t), []byte("\xe6"), []byte("\xc3\xa6"), -1)
	fmt.Printf("%s\n", bytes.Replace([]byte(y), []byte("\xc6"), []byte("\xc3\x86"), -1))

}
<|endoftext|>


<commit_before>// Tideland Go Cells - Unit Tests
//
// Copyright (C) 2010-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package cells_test

//--------------------
// IMPORTS
//--------------------

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/tideland/golib/audit"

	"github.com/tideland/gocells/cells"
)

//--------------------
// TESTS
//--------------------

// TestPositiveWaitPayload waits for a payload.
func TestPositiveWaitPayload(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	waiter := cells.NewPayloadWaiter()

	go func() {
		time.Sleep(250 * time.Millisecond)
		waiter.Set(cells.NewPayload(4711))
		waiter.Set(cells.NewPayload(1174))
	}()

	ctx := context.Background()
	payload, err := waiter.Wait(ctx)
	assert.Nil(err)
	set, ok := payload.GetInt(cells.DefaultPayload)
	assert.True(ok)
	assert.Equal(set, 4711)
}

// TestWaitPayloadTimeout waits for a payload but
// timeout is faster.
func TestWaitPayloadTimeout(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	waiter := cells.NewPayloadWaiter()

	go func() {
		time.Sleep(500 * time.Millisecond)
		waiter.Set(cells.NewPayload(4711))
	}()

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	payload, err := waiter.Wait(ctx)
	cancel()
	assert.ErrorMatch(err, "context deadline exceeded")
	assert.Nil(payload)
}

// TestWaitPayloadCancel waits for a payload but
// it's canceled earlier.
func TestWaitPayloadCancel(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	waiter := cells.NewPayloadWaiter()

	go func() {
		time.Sleep(500 * time.Millisecond)
		waiter.Set(cells.NewPayload(4711))
	}()

	ctx, cancel := context.WithCancel(context.Background())
	time.AfterFunc(100*time.Millisecond, func() {
		cancel()
	})
	payload, err := waiter.Wait(ctx)
	assert.ErrorMatch(err, "context canceled")
	assert.Nil(payload)
}

// TestEventSink tests the simple event sink.
func TestEventSink(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	checkTopic := func(event cells.Event) {
		assert.Contents(event.Topic(), topics)
	}

	// Empty sink.
	sink := cells.NewEventSink(0)
	first, ok := sink.First()
	assert.Nil(first)
	assert.False(ok)
	last, ok := sink.Last()
	assert.Nil(last)
	assert.False(ok)
	at, ok := sink.At(-1)
	assert.Nil(at)
	assert.False(ok)
	at, ok = sink.At(4711)
	assert.Nil(at)
	assert.False(ok)

	// Limited number of events.
	sink = cells.NewEventSink(5)
	addEvents(assert, 10, sink)
	assert.Length(sink, 5)

	// Unlimited number of events.
	sink = cells.NewEventSink(0)
	addEvents(assert, 10, sink)
	assert.Length(sink, 10)

	first, ok = sink.First()
	assert.True(ok)
	checkTopic(first)
	last, ok = sink.Last()
	assert.True(ok)
	checkTopic(last)

	for i := 0; i < sink.Len(); i++ {
		at, ok = sink.At(i)
		assert.True(ok)
		checkTopic(at)
	}
}

// TestEventSinkIteration tests the event sink iteration.
func TestEventSinkIteration(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	sink := cells.NewEventSink(0)
	addEvents(assert, 10, sink)

	assert.Length(sink, 10)
	err := sink.Do(func(index int, event cells.Event) error {
		assert.Contents(event.Topic(), topics)
		payload, ok := event.Payload().GetInt(cells.DefaultPayload)
		assert.True(ok)
		assert.Range(payload, 1, 10)
		return nil
	})
	assert.Nil(err)
	ok, err := sink.Match(func(index int, event cells.Event) (bool, error) {
		topicOK := event.Topic() >= "a" && event.Topic() <= "j"
		payload, ok := event.Payload().GetInt(cells.DefaultPayload)
		payloadOK := payload >= 1 && payload <= 10
		return topicOK && ok && payloadOK, nil
	})
	assert.Nil(err)
	assert.True(ok)
}

// TestEventSinkIterationError tests the event sink iteration error.
func TestEventSinkIterationError(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	sink := cells.NewEventSink(0)
	addEvents(assert, 10, sink)

	err := sink.Do(func(index int, event cells.Event) error {
		return errors.New("ouch")
	})
	assert.ErrorMatch(err, "ouch")
	ok, err := sink.Match(func(index int, event cells.Event) (bool, error) {
		// The bool true won't be passed to outside.
		return true, errors.New("ouch")
	})
	assert.False(ok)
	assert.ErrorMatch(err, "ouch")
}

// TestCheckedEventSink tests the notification of a waiter
// when a criterion in the sink matches.
func TestCheckedEventSink(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	checker := func(events cells.EventSinkIterator) (bool, cells.Payload, error) {
		wanted := []string{"f", "c", "c"}
		ok, err := events.Match(func(index int, event cells.Event) (bool, error) {
			return event.Topic() == wanted[index], nil
		})
		if err != nil {
			return false, nil, err
		}
		if ok {
			return true, cells.NewPayload("fcc"), nil
		}
		return false, nil, nil
	}
	sink, waiter := cells.NewCheckedEventSink(3, checker)

	go addEvents(assert, 100, sink)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	payload, err := waiter.Wait(ctx)
	assert.Nil(err)
	wanted, ok := payload.GetString(cells.DefaultPayload)
	assert.True(ok)
	assert.Equal(wanted, "fcc")
	cancel()
}

//--------------------
// HELPER
//--------------------

// topics contains the test topics.
var topics = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}

// addEvents adds a number of events to a sink.
func addEvents(assert audit.Assertion, count int, sink cells.EventSink) {
	generator := audit.NewGenerator(audit.FixedRand())
	for i := 0; i < count; i++ {
		topic := generator.OneStringOf(topics...)
		payload := generator.Int(1, 10)
		event, err := cells.NewEvent(nil, topic, payload)
		assert.Nil(err)
		n, err := sink.Add(event)
		assert.Nil(err)
		assert.True(n > 0)
	}
}

// EOF
<commit_msg>Added test for failing event sink<commit_after>// Tideland Go Cells - Unit Tests
//
// Copyright (C) 2010-2017 Frank Mueller / Tideland / Oldenburg / Germany
//
// All rights reserved. Use of this source code is governed
// by the new BSD license.

package cells_test

//--------------------
// IMPORTS
//--------------------

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/tideland/golib/audit"

	"github.com/tideland/gocells/cells"
)

//--------------------
// TESTS
//--------------------

// TestPositiveWaitPayload waits for a payload.
func TestPositiveWaitPayload(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	waiter := cells.NewPayloadWaiter()

	go func() {
		time.Sleep(250 * time.Millisecond)
		waiter.Set(cells.NewPayload(4711))
		waiter.Set(cells.NewPayload(1174))
	}()

	ctx := context.Background()
	payload, err := waiter.Wait(ctx)
	assert.Nil(err)
	set, ok := payload.GetInt(cells.DefaultPayload)
	assert.True(ok)
	assert.Equal(set, 4711)
}

// TestWaitPayloadTimeout waits for a payload but
// timeout is faster.
func TestWaitPayloadTimeout(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	waiter := cells.NewPayloadWaiter()

	go func() {
		time.Sleep(500 * time.Millisecond)
		waiter.Set(cells.NewPayload(4711))
	}()

	ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
	payload, err := waiter.Wait(ctx)
	cancel()
	assert.ErrorMatch(err, "context deadline exceeded")
	assert.Nil(payload)
}

// TestWaitPayloadCancel waits for a payload but
// it's canceled earlier.
func TestWaitPayloadCancel(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	waiter := cells.NewPayloadWaiter()

	go func() {
		time.Sleep(500 * time.Millisecond)
		waiter.Set(cells.NewPayload(4711))
	}()

	ctx, cancel := context.WithCancel(context.Background())
	time.AfterFunc(100*time.Millisecond, func() {
		cancel()
	})
	payload, err := waiter.Wait(ctx)
	assert.ErrorMatch(err, "context canceled")
	assert.Nil(payload)
}

// TestEventSink tests the simple event sink.
func TestEventSink(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	checkTopic := func(event cells.Event) {
		assert.Contents(event.Topic(), topics)
	}

	// Empty sink.
	sink := cells.NewEventSink(0)
	first, ok := sink.First()
	assert.Nil(first)
	assert.False(ok)
	last, ok := sink.Last()
	assert.Nil(last)
	assert.False(ok)
	at, ok := sink.At(-1)
	assert.Nil(at)
	assert.False(ok)
	at, ok = sink.At(4711)
	assert.Nil(at)
	assert.False(ok)

	// Limited number of events.
	sink = cells.NewEventSink(5)
	addEvents(assert, 10, sink)
	assert.Length(sink, 5)

	// Unlimited number of events.
	sink = cells.NewEventSink(0)
	addEvents(assert, 10, sink)
	assert.Length(sink, 10)

	first, ok = sink.First()
	assert.True(ok)
	checkTopic(first)
	last, ok = sink.Last()
	assert.True(ok)
	checkTopic(last)

	for i := 0; i < sink.Len(); i++ {
		at, ok = sink.At(i)
		assert.True(ok)
		checkTopic(at)
	}
}

// TestEventSinkIteration tests the event sink iteration.
func TestEventSinkIteration(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	sink := cells.NewEventSink(0)
	addEvents(assert, 10, sink)

	assert.Length(sink, 10)
	err := sink.Do(func(index int, event cells.Event) error {
		assert.Contents(event.Topic(), topics)
		payload, ok := event.Payload().GetInt(cells.DefaultPayload)
		assert.True(ok)
		assert.Range(payload, 1, 10)
		return nil
	})
	assert.Nil(err)
	ok, err := sink.Match(func(index int, event cells.Event) (bool, error) {
		topicOK := event.Topic() >= "a" && event.Topic() <= "j"
		payload, ok := event.Payload().GetInt(cells.DefaultPayload)
		payloadOK := payload >= 1 && payload <= 10
		return topicOK && ok && payloadOK, nil
	})
	assert.Nil(err)
	assert.True(ok)
}

// TestEventSinkIterationError tests the event sink iteration error.
func TestEventSinkIterationError(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	sink := cells.NewEventSink(0)
	addEvents(assert, 10, sink)

	err := sink.Do(func(index int, event cells.Event) error {
		return errors.New("ouch")
	})
	assert.ErrorMatch(err, "ouch")
	ok, err := sink.Match(func(index int, event cells.Event) (bool, error) {
		// The bool true won't be passed to outside.
		return true, errors.New("ouch")
	})
	assert.False(ok)
	assert.ErrorMatch(err, "ouch")
}

// TestCheckedEventSink tests the notification of a waiter
// when a criterion in the sink matches.
func TestCheckedEventSink(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	checker := func(events cells.EventSinkIterator) (bool, cells.Payload, error) {
		wanted := []string{"f", "c", "c"}
		ok, err := events.Match(func(index int, event cells.Event) (bool, error) {
			return event.Topic() == wanted[index], nil
		})
		if err != nil {
			return false, nil, err
		}
		if ok {
			return true, cells.NewPayload("fcc"), nil
		}
		return false, nil, nil
	}
	sink, waiter := cells.NewCheckedEventSink(3, checker)

	go addEvents(assert, 100, sink)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	payload, err := waiter.Wait(ctx)
	assert.Nil(err)
	wanted, ok := payload.GetString(cells.DefaultPayload)
	assert.True(ok)
	assert.Equal(wanted, "fcc")
	cancel()
}

// TestCheckedEventSinkFailing tests the missing notification of a waiter
// when a criterion in the sink does not match.
func TestCheckedEventSinkFailing(t *testing.T) {
	assert := audit.NewTestingAssertion(t, true)
	checker := func(events cells.EventSinkIterator) (bool, cells.Payload, error) {
		return false, nil, nil
	}
	sink, waiter := cells.NewCheckedEventSink(3, checker)

	go addEvents(assert, 100, sink)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	payload, err := waiter.Wait(ctx)
	assert.Nil(payload)
	assert.ErrorMatch(err, "context deadline exceeded")
	cancel()
}

//--------------------
// HELPER
//--------------------

// topics contains the test topics.
var topics = []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"}

// addEvents adds a number of events to a sink.
func addEvents(assert audit.Assertion, count int, sink cells.EventSink) {
	generator := audit.NewGenerator(audit.FixedRand())
	for i := 0; i < count; i++ {
		topic := generator.OneStringOf(topics...)
		payload := generator.Int(1, 10)
		event, err := cells.NewEvent(nil, topic, payload)
		assert.Nil(err)
		n, err := sink.Add(event)
		assert.Nil(err)
		assert.True(n > 0)
	}
}

// EOF
<|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 text

import (
	"fmt"

	"github.com/mum4k/termdash/keyboard"
	"github.com/mum4k/termdash/mouse"
	"github.com/mum4k/termdash/private/wrap"
)

// options.go contains configurable options for Text.

// Option is used to provide options to New().
type Option interface {
	// set sets the provided option.
	set(*options)
}

// options stores the provided options.
type options struct {
	wrapMode         wrap.Mode
	rollContent      bool
	disableScrolling bool
	mouseUpButton    mouse.Button
	mouseDownButton  mouse.Button
	keyUp            keyboard.Key
	keyDown          keyboard.Key
	keyPgUp          keyboard.Key
	keyPgDown        keyboard.Key
}

// newOptions returns a new options instance.
func newOptions(opts ...Option) *options {
	opt := &options{
		mouseUpButton:   DefaultScrollMouseButtonUp,
		mouseDownButton: DefaultScrollMouseButtonDown,
		keyUp:           DefaultScrollKeyUp,
		keyDown:         DefaultScrollKeyDown,
		keyPgUp:         DefaultScrollKeyPageUp,
		keyPgDown:       DefaultScrollKeyPageDown,
	}
	for _, o := range opts {
		o.set(opt)
	}
	return opt
}

// validate validates the provided options.
func (o *options) validate() error {
	keys := map[keyboard.Key]bool{
		o.keyUp:     true,
		o.keyDown:   true,
		o.keyPgUp:   true,
		o.keyPgDown: true,
	}
	if len(keys) != 4 {
		return fmt.Errorf("invalid ScrollKeys(up:%v, down:%v, pageUp:%v, pageDown:%v), the keys must be unique", o.keyUp, o.keyDown, o.keyPgUp, o.keyPgDown)
	}
	if o.mouseUpButton == o.mouseDownButton {
		return fmt.Errorf("invalid ScrollMouseButtons(up:%v, down:%v), the buttons must be unique", o.mouseUpButton, o.mouseDownButton)
	}
	return nil
}

// option implements Option.
type option func(*options)

// set implements Option.set.
func (o option) set(opts *options) {
	o(opts)
}

// WrapAtWords configures the text widget so that it automatically wraps lines
// that are longer than the width of the widget at word boundaries. If not
// provided, long lines are trimmed instead.
func WrapAtWords() Option {
	return option(func(opts *options) {
		opts.wrapMode = wrap.AtWords
	})
}

// WrapAtRunes configures the text widget so that it automatically wraps lines
// that are longer than the width of the widget at rune boundaries. If not
// provided, long lines are trimmed instead.
func WrapAtRunes() Option {
	return option(func(opts *options) {
		opts.wrapMode = wrap.AtRunes
	})
}

// RollContent configures the text widget so that it rolls the text content up
// if more text than the size of the container is added. If not provided, the
// content is trimmed instead.
func RollContent() Option {
	return option(func(opts *options) {
		opts.rollContent = true
	})
}

// DisableScrolling disables the scrolling of the content using keyboard and
// mouse.
func DisableScrolling() Option {
	return option(func(opts *options) {
		opts.disableScrolling = true
	})
}

// The default mouse buttons for content scrolling.
const (
	DefaultScrollMouseButtonUp   = mouse.ButtonWheelUp
	DefaultScrollMouseButtonDown = mouse.ButtonWheelDown
)

// ScrollMouseButtons configures the mouse buttons that scroll the content.
// The provided buttons must be unique, e.g. the same button cannot be both up
// and down.
func ScrollMouseButtons(up, down mouse.Button) Option {
	return option(func(opts *options) {
		opts.mouseUpButton = up
		opts.mouseDownButton = down
	})
}

// The default keys for content scrolling.
const (
	DefaultScrollKeyUp       = keyboard.KeyArrowUp
	DefaultScrollKeyDown     = keyboard.KeyArrowDown
	DefaultScrollKeyPageUp   = keyboard.KeyPgUp
	DefaultScrollKeyPageDown = keyboard.KeyPgDn
)

// ScrollKeys configures the keyboard keys that scroll the content.
// The provided keys must be unique, e.g. the same key cannot be both up and
// down.
func ScrollKeys(up, down, pageUp, pageDown keyboard.Key) Option {
	return option(func(opts *options) {
		opts.keyUp = up
		opts.keyDown = down
		opts.keyPgUp = pageUp
		opts.keyPgDown = pageDown
	})
}
<commit_msg>added the ability to swap the scrolling runes<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 text

import (
	"fmt"

	"github.com/mum4k/termdash/keyboard"
	"github.com/mum4k/termdash/mouse"
	"github.com/mum4k/termdash/private/wrap"
)

// options.go contains configurable options for Text.

// Option is used to provide options to New().
type Option interface {
	// set sets the provided option.
	set(*options)
}

// options stores the provided options.
type options struct {
	scrollUp	 rune
	scrollDown	 rune
	wrapMode         wrap.Mode
	rollContent      bool
	disableScrolling bool
	mouseUpButton    mouse.Button
	mouseDownButton  mouse.Button
	keyUp            keyboard.Key
	keyDown          keyboard.Key
	keyPgUp          keyboard.Key
	keyPgDown        keyboard.Key
}

// newOptions returns a new options instance.
func newOptions(opts ...Option) *options {
	opt := &options{
		scrollUp: 	 DefaultScrollUpRune,
		scrollDown: 	 DefaultScrollDownRune,
		mouseUpButton:   DefaultScrollMouseButtonUp,
		mouseDownButton: DefaultScrollMouseButtonDown,
		keyUp:           DefaultScrollKeyUp,
		keyDown:         DefaultScrollKeyDown,
		keyPgUp:         DefaultScrollKeyPageUp,
		keyPgDown:       DefaultScrollKeyPageDown,
	}
	for _, o := range opts {
		o.set(opt)
	}
	return opt
}

// validate validates the provided options.
func (o *options) validate() error {
	keys := map[keyboard.Key]bool{
		o.keyUp:     true,
		o.keyDown:   true,
		o.keyPgUp:   true,
		o.keyPgDown: true,
	}
	if len(keys) != 4 {
		return fmt.Errorf("invalid ScrollKeys(up:%v, down:%v, pageUp:%v, pageDown:%v), the keys must be unique", o.keyUp, o.keyDown, o.keyPgUp, o.keyPgDown)
	}
	if o.mouseUpButton == o.mouseDownButton {
		return fmt.Errorf("invalid ScrollMouseButtons(up:%v, down:%v), the buttons must be unique", o.mouseUpButton, o.mouseDownButton)
	}
	return nil
}

// option implements Option.
type option func(*options)

// set implements Option.set.
func (o option) set(opts *options) {
	o(opts)
}

// ScrollRunes configures the text widgets scroll runes, shown at the top and
// bottom of a scrollable text widget. If not provided, the default scroll
// runes will be used.
func ScrollRunes(up, down rune) Option {
	return option(func(opts *options) {
		opts.scrollUp = up
		opts.scrollDown = down
	})
}

// The default scroll runes for content scrolling
const (
	DefaultScrollUpRune = '⇧'
	DefaultScrollDownRune = '⇩'
)

// WrapAtWords configures the text widget so that it automatically wraps lines
// that are longer than the width of the widget at word boundaries. If not
// provided, long lines are trimmed instead.
func WrapAtWords() Option {
	return option(func(opts *options) {
		opts.wrapMode = wrap.AtWords
	})
}

// WrapAtRunes configures the text widget so that it automatically wraps lines
// that are longer than the width of the widget at rune boundaries. If not
// provided, long lines are trimmed instead.
func WrapAtRunes() Option {
	return option(func(opts *options) {
		opts.wrapMode = wrap.AtRunes
	})
}

// RollContent configures the text widget so that it rolls the text content up
// if more text than the size of the container is added. If not provided, the
// content is trimmed instead.
func RollContent() Option {
	return option(func(opts *options) {
		opts.rollContent = true
	})
}

// DisableScrolling disables the scrolling of the content using keyboard and
// mouse.
func DisableScrolling() Option {
	return option(func(opts *options) {
		opts.disableScrolling = true
	})
}

// The default mouse buttons for content scrolling.
const (
	DefaultScrollMouseButtonUp   = mouse.ButtonWheelUp
	DefaultScrollMouseButtonDown = mouse.ButtonWheelDown
)

// ScrollMouseButtons configures the mouse buttons that scroll the content.
// The provided buttons must be unique, e.g. the same button cannot be both up
// and down.
func ScrollMouseButtons(up, down mouse.Button) Option {
	return option(func(opts *options) {
		opts.mouseUpButton = up
		opts.mouseDownButton = down
	})
}

// The default keys for content scrolling.
const (
	DefaultScrollKeyUp       = keyboard.KeyArrowUp
	DefaultScrollKeyDown     = keyboard.KeyArrowDown
	DefaultScrollKeyPageUp   = keyboard.KeyPgUp
	DefaultScrollKeyPageDown = keyboard.KeyPgDn
)

// ScrollKeys configures the keyboard keys that scroll the content.
// The provided keys must be unique, e.g. the same key cannot be both up and
// down.
func ScrollKeys(up, down, pageUp, pageDown keyboard.Key) Option {
	return option(func(opts *options) {
		opts.keyUp = up
		opts.keyDown = down
		opts.keyPgUp = pageUp
		opts.keyPgDown = pageDown
	})
}
<|endoftext|>


<commit_before>package slack

import (
	"testing"
)

func TestOnEvent(t *testing.T) {
	bot := NewBot("token")
	bot.OnEvent("message", shutdownHandler)
	if len(bot.Handlers) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i", len(bot.Handlers))
	}
	if len(bot.Handlers["message"]) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i", len(bot.Handlers["message"]))
	}

	handler := bot.Handlers["message"][0]
	_, status := handler(bot, nil)
	if status != Shutdown {
		t.Error("Error. Found a different handler")
	}

	h := func(_ *Bot, _ map[string]interface{}) (*Message, Status) {
		return nil, ShutdownNow
	}
	bot.OnEvent("user_typing", h)
	if len(bot.Handlers) != 2 {
		t.Errorf(
			"Error. Expecting 2 event types to have handlers. Got %i",
			len(bot.Handlers),
		)
	}
	actualH := bot.Handlers["user_typing"][0]
	_, status = actualH(nil, nil)
	if status != ShutdownNow {
		t.Error("Error. Found a different handler")
	}
	bot.OnEvent("user_typing", h)
	userTypingHandlers := bot.Handlers["user_typing"]
	if len(userTypingHandlers) != 2 {
		t.Errorf(
			"Error. Expecting 2 handlers for \"user_typing\" events. Found %i.",
			len(userTypingHandlers),
		)
	}
}

func TestOnEventWithSubtype(t *testing.T) {
	bot := NewBot("token")
	bot.OnEventWithSubtype("message", "channel_join", shutdownHandler)
	if len(bot.Subhandlers) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i", len(bot.Subhandlers))
	}
	if len(bot.Subhandlers["message"]["channel_join"]) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i",
			len(bot.Subhandlers["message"]["channel_join"]))
	}

	handler := bot.Subhandlers["message"]["channel_join"][0]
	_, status := handler(nil, nil)
	if status != Shutdown {
		t.Error("Error. Found a different handler.")
	}

	bot.OnEventWithSubtype("message", "channel_join", shutdownHandler)
	channelJoinHandlers := bot.Subhandlers["message"]["channel_join"]
	if len(channelJoinHandlers) != 2 {
		t.Errorf("Error. Expecting 2 handlers. Found %i.",
			len(channelJoinHandlers))
	}
}

func TestPrivate_handle_noSubtype(t *testing.T) {
	event := map[string]interface{}{"type": "message"}
	// No handlers
	bot := NewBot("token")
	wrappers := bot.handle(event)
	if len(wrappers) != 0 {
		t.Errorf("Error. Expecting 0 wrappers. Found %i.", len(wrappers))
	}

	// one handler
	h1 := func(_ *Bot, _ map[string]interface{}) (*Message, Status) {
		return nil, Shutdown
	}

	bot.OnEvent("message", h1)
	wrappers = bot.handle(event)
	if len(wrappers) != 1 {
		t.Errorf("Error. Expecting 1 wrapper. Found %i.", len(wrappers))
	}
	if wrappers[0].status != Shutdown {
		t.Errorf("Error. Expecting status %i. Found %i.", Shutdown,
			wrappers[0].status)
	}

	// two handlers
	h2 := func(_ *Bot, _ map[string]interface{}) (*Message, Status) {
		return nil, ShutdownNow
	}
	bot.OnEvent("message", h2)
	wrappers = bot.handle(event)
	if len(wrappers) != 2 {
		t.Errorf("Error. Expecting 2 wrappers. Found %i.", len(wrappers))
	}
}

func TestPrivate_handle_subtype(t *testing.T) {
}
<commit_msg>more handling tests<commit_after>package slack

import (
	"testing"
)

func TestOnEvent(t *testing.T) {
	bot := NewBot("token")
	bot.OnEvent("message", shutdownHandler)
	if len(bot.Handlers) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i", len(bot.Handlers))
	}
	if len(bot.Handlers["message"]) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i", len(bot.Handlers["message"]))
	}

	handler := bot.Handlers["message"][0]
	_, status := handler(bot, nil)
	if status != Shutdown {
		t.Error("Error. Found a different handler")
	}

	h := func(_ *Bot, _ map[string]interface{}) (*Message, Status) {
		return nil, ShutdownNow
	}
	bot.OnEvent("user_typing", h)
	if len(bot.Handlers) != 2 {
		t.Errorf(
			"Error. Expecting 2 event types to have handlers. Got %i",
			len(bot.Handlers),
		)
	}
	actualH := bot.Handlers["user_typing"][0]
	_, status = actualH(nil, nil)
	if status != ShutdownNow {
		t.Error("Error. Found a different handler")
	}
	bot.OnEvent("user_typing", h)
	userTypingHandlers := bot.Handlers["user_typing"]
	if len(userTypingHandlers) != 2 {
		t.Errorf(
			"Error. Expecting 2 handlers for \"user_typing\" events. Found %i.",
			len(userTypingHandlers),
		)
	}
}

func TestOnEventWithSubtype(t *testing.T) {
	bot := NewBot("token")
	bot.OnEventWithSubtype("message", "channel_join", shutdownHandler)
	if len(bot.Subhandlers) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i", len(bot.Subhandlers))
	}
	if len(bot.Subhandlers["message"]["channel_join"]) != 1 {
		t.Errorf("Error. Expecting 1 handler. Got %i",
			len(bot.Subhandlers["message"]["channel_join"]))
	}

	handler := bot.Subhandlers["message"]["channel_join"][0]
	_, status := handler(nil, nil)
	if status != Shutdown {
		t.Error("Error. Found a different handler.")
	}

	bot.OnEventWithSubtype("message", "channel_join", shutdownHandler)
	channelJoinHandlers := bot.Subhandlers["message"]["channel_join"]
	if len(channelJoinHandlers) != 2 {
		t.Errorf("Error. Expecting 2 handlers. Found %i.",
			len(channelJoinHandlers))
	}
}

func TestPrivate_handle_noSubtype(t *testing.T) {
	event := map[string]interface{}{"type": "message"}
	// No handlers
	bot := NewBot("token")
	wrappers := bot.handle(event)
	if len(wrappers) != 0 {
		t.Errorf("Error. Expecting 0 wrappers. Found %i.", len(wrappers))
	}

	// one handler
	h1 := func(_ *Bot, _ map[string]interface{}) (*Message, Status) {
		return nil, Shutdown
	}

	bot.OnEvent("message", h1)
	wrappers = bot.handle(event)
	if len(wrappers) != 1 {
		t.Errorf("Error. Expecting 1 wrapper. Found %i.", len(wrappers))
	}
	if wrappers[0].status != Shutdown {
		t.Errorf("Error. Expecting status %i. Found %i.", Shutdown,
			wrappers[0].status)
	}

	// two handlers
	h2 := func(_ *Bot, _ map[string]interface{}) (*Message, Status) {
		return nil, ShutdownNow
	}
	bot.OnEvent("message", h2)
	wrappers = bot.handle(event)
	if len(wrappers) != 2 {
		t.Errorf("Error. Expecting 2 wrappers. Found %i.", len(wrappers))
	}
}

func TestPrivate_handle_subtype(t *testing.T) {
	event := map[string]interface{}{
		"type":    "message",
		"subtype": "channel_join",
	}
	bot := NewBot("token")

	assert := func(expected, actual int, t *testing.T) {
		if expected != actual {
			t.Errorf("Error. Expecting %i. Got %i.", expected, actual)
		}
	}

	// no handlers
	wrappers := bot.handle(event)
	assert(0, len(wrappers), t)

	// one subevent handler
	bot.OnEventWithSubtype("message", "channel_join", shutdownHandler)
	wrappers = bot.handle(event)
	assert(1, len(wrappers), t)

	// one relevant, one irrelevant
	bot.OnEventWithSubtype("message", "not_relevant", shutdownHandler)
	wrappers = bot.handle(event)
	assert(1, len(wrappers), t)

	// adding regular event handler
	bot.OnEvent("message", shutdownHandler)
	wrappers = bot.handle(event)
	assert(2, len(wrappers), t)

	// second subevent handler
	bot.OnEventWithSubtype("message", "channel_join", shutdownHandler)
	wrappers = bot.handle(event)
	assert(3, len(wrappers), t)
}

func TestPrivate_handle_noType(t *testing.T) {
	event := map[string]interface{}{"foo": "bar"}
	bot := NewBot("token")
	bot.OnEvent("bar", shutdownHandler)
	wrappers := bot.handle(event)
	if len(wrappers) != 0 {
		t.Errorf("Error. Expecting 0 wrappers. Found %i.", len(wrappers))
	}
}
<|endoftext|>