<commit_before>package effio

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

func (cmd *Cmd) Inventory() {
	devs := InventoryDevs()
	sort.Sort(devs)
	js, err := json.MarshalIndent(devs, "", "  ")
	if err != nil {
		log.Fatalf("Failed to encode inventory JSON: %s\n", err)
	}

	fmt.Println(string(js))
}

// in my tests I use whole devices with a single GPT partition and the
// ext4 filesystem (for now). This finds all the devices and grabs most
// of the info needed for the device JSON file and dumps it to stdout
// so it can be put in a file and edited to taste.
func InventoryDevs() (devs Devices) {
	visitor := func(dpath string, f os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("Encountered an error while inventorying devices '%s': %s", dpath, err)
		}

		fi, err := os.Stat(dpath)
		if err != nil {
			log.Fatal(err)
		}

		// ignore anything that's not a device, os.Stat seems to follow the
		// link and set mode device which is perfect for this case
		if fi.Mode()&os.ModeDevice == 0 {
			return nil
		}

		device := path.Base(dpath)
		reldev, err := os.Readlink(dpath)
		if err != nil {
			log.Fatal(err)
		}
		letter := path.Base(reldev)

		// only consider devices with a partition table and only
		// consider partition 1
		if !strings.HasSuffix(device, "-part1") {
			return nil
		}

		bdev := strings.TrimRight(letter, "1234567890")
		model := GetSysBlockString(bdev, "device/model")
		bsize := GetSysBlockInt(bdev, "queue/hw_sector_size")
		size := GetSysBlockInt(bdev, "size") * bsize
		rotational := GetSysBlockInt(bdev, "queue/rotational")
		brand := GuessBrand(model)
		// lower case, replace spaces and dashes with underscore
		name := strings.Replace(strings.Replace(strings.ToLower(model), " ", "_", -1), "-", "_", -1)

		d := Device{
			Name:       name,
			Device:     dpath,
			Mountpoint: path.Join("/mnt/effio", name),
			Filesystem: "ext4",
			Brand:      brand,
			Series:     model,
			Capacity:   size,
			Rotational: (rotational == 1),
			Transport:  "", // can be detected but it's a lot of work
			HBA:        "", // ditto
			Media:      "", // no way to detect
			Blocksize:  int(bsize),
			RPM:        0, // no way to detect?
		}

		devs = append(devs, d)

		return nil
	}

	err := filepath.Walk("/dev/disk/by-id", visitor)
	if err != nil {
		log.Fatalf("Could not inventory devices in /dev/disk/by-id: %s", err)
	}

	return devs
}

func GetSysBlockString(device string, fpath string) string {
	sbpath := path.Join("/sys/block", device, fpath)
	data, err := ioutil.ReadFile(sbpath)
	if err != nil {
		log.Fatal(err)
	}

	return strings.TrimRight(string(data), " \t\r\n")
}

func GetSysBlockInt(device string, fpath string) int64 {
	str := GetSysBlockString(device, fpath)
	out, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		log.Fatal(err)
	}
	return out
}

func GuessBrand(model string) string {
	if strings.HasPrefix(model, "Samsung") {
		return "Samsung"
	} else if strings.HasPrefix(model, "ST") {
		return "Seagate"
	} else if strings.HasPrefix(model, "WD") {
		return "Western Digital"
	} else if strings.HasPrefix(model, "MRD") {
		return "I/O Switch"
	} else if strings.HasPrefix(model, "SSD") {
		return "PNY"
	} else {
		return "Generic"
	}
}
<commit_msg>add -path to inventory<commit_after>package effio

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
)

func (cmd *Cmd) Inventory() {
	// for some reason by-id doesn't show up on VMware Fusion
	// this allows for pointing at by-path or by-uuid instead
	var devPathFlag string
	cmd.FlagSet.StringVar(&devPathFlag, "path", "/dev/disk/by-id", "dev path to search for devices")
	cmd.FlagSet.Parse(cmd.Args)

	// load device data from json
	devs := InventoryDevs(devPathFlag)
	sort.Sort(devs)
	js, err := json.MarshalIndent(devs, "", "  ")
	if err != nil {
		log.Fatalf("Failed to encode inventory JSON: %s\n", err)
	}

	fmt.Println(string(js))
}

// in my tests I use whole devices with a single GPT partition and the
// ext4 filesystem (for now). This finds all the devices and grabs most
// of the info needed for the device JSON file and dumps it to stdout
// so it can be put in a file and edited to taste.
func InventoryDevs(dpath string) (devs Devices) {
	visitor := func(dpath string, f os.FileInfo, err error) error {
		if err != nil {
			log.Fatalf("Encountered an error while inventorying devices '%s': %s", dpath, err)
		}

		fi, err := os.Stat(dpath)
		if err != nil {
			log.Fatal(err)
		}

		// ignore anything that's not a device, os.Stat seems to follow the
		// link and set mode device which is perfect for this case
		if fi.Mode()&os.ModeDevice == 0 {
			return nil
		}

		device := path.Base(dpath)
		reldev, err := os.Readlink(dpath)
		if err != nil {
			log.Fatal(err)
		}
		letter := path.Base(reldev)

		// only consider devices with a partition table and only
		// consider partition 1
		if !strings.HasSuffix(device, "-part1") {
			return nil
		}

		bdev := strings.TrimRight(letter, "1234567890")
		model := GetSysBlockString(bdev, "device/model")
		bsize := GetSysBlockInt(bdev, "queue/hw_sector_size")
		size := GetSysBlockInt(bdev, "size") * bsize
		rotational := GetSysBlockInt(bdev, "queue/rotational")
		brand := GuessBrand(model)
		// lower case, replace spaces and dashes with underscore
		name := strings.Replace(strings.Replace(strings.ToLower(model), " ", "_", -1), "-", "_", -1)

		d := Device{
			Name:       name,
			Device:     dpath,
			Mountpoint: path.Join("/mnt/effio", name),
			Filesystem: "ext4",
			Brand:      brand,
			Series:     model,
			Capacity:   size,
			Rotational: (rotational == 1),
			Transport:  "", // can be detected but it's a lot of work
			HBA:        "", // ditto
			Media:      "", // no way to detect
			Blocksize:  int(bsize),
			RPM:        0, // no way to detect?
		}

		devs = append(devs, d)

		return nil
	}

	err := filepath.Walk(dpath, visitor)
	if err != nil {
		log.Fatalf("Could not inventory devices in /dev/disk/by-id: %s", err)
	}

	return devs
}

func GetSysBlockString(device string, fpath string) string {
	sbpath := path.Join("/sys/block", device, fpath)
	data, err := ioutil.ReadFile(sbpath)
	if err != nil {
		log.Fatal(err)
	}

	return strings.TrimRight(string(data), " \t\r\n")
}

func GetSysBlockInt(device string, fpath string) int64 {
	str := GetSysBlockString(device, fpath)
	out, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		log.Fatal(err)
	}
	return out
}

func GuessBrand(model string) string {
	if strings.HasPrefix(model, "Samsung") {
		return "Samsung"
	} else if strings.HasPrefix(model, "ST") {
		return "Seagate"
	} else if strings.HasPrefix(model, "WD") {
		return "Western Digital"
	} else if strings.HasPrefix(model, "MRD") {
		return "I/O Switch"
	} else if strings.HasPrefix(model, "SSD") {
		return "PNY"
	} else {
		return "Generic"
	}
}
<|endoftext|>


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

import (
	"encoding/json"
	"encoding/gob"
	"log"
	"flag"
	"net/http"
	"os"
	"sync"
	"time"
)

var (
	bookmarkFn = flag.String("bookmarkFn", "/tmp/bookmark.db",
		"location to store bookmarked links")
)

func init() {
	b := NewBookmarks()
	http.HandleFunc("/bookmarks", func(w http.ResponseWriter, r *http.Request) {
		b.ListHandler(w, r)
	})
	http.HandleFunc("/bookmarks/add", func(w http.ResponseWriter, r *http.Request) {
		b.AddHandler(w, r)
	})
}

type Bookmark struct {
	Name string `json:"name"`
	Url string `json:"url"`
	Added time.Time `json:"added"`
}

type Bookmarks struct {
	Bookmarks []Bookmark
	addCh chan Bookmark
	mu   sync.RWMutex
}

func NewBookmarks() *Bookmarks {
	b := &Bookmarks{
		Bookmarks: make([]Bookmark, 0),
		addCh: make(chan Bookmark, 1),
	}
	go b.Loop()
	return b
}

func (b *Bookmarks) Add(bookmark Bookmark) {
	log.Print("Adding bookmark ", bookmark)
	b.addCh <- bookmark
}

func (b *Bookmarks) Load(fn string) error {
	log.Print("Loading bookmarks from ", fn)

	f, err := os.Open(fn)
	if err != nil {
		return err
	}
	defer f.Close()

	gf := gob.NewDecoder(f)
	err = gf.Decode(&b.Bookmarks)
	return err
}

func (b *Bookmarks) Save(fn string) error {
	log.Print("Saving bookmarks to ", fn)
	f, err := os.OpenFile(fn, os.O_WRONLY | os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	gf := gob.NewEncoder(f)
	err = gf.Encode(b.Bookmarks)
	return err
}

func (b *Bookmarks) Loop() {
	err := b.Load(*bookmarkFn)
	if err != nil {
		log.Print("Error loading bookmarks ", err)
	}
	for {
		select {
		case bookmark := <-b.addCh:
			b.mu.Lock()
			b.Bookmarks = append(b.Bookmarks, bookmark)
			// TODO This doesn't really scale
			err = b.Save(*bookmarkFn)
			if err != nil {
				log.Print("Error saving bookmarks ", err)
			}
			b.mu.Unlock()
		}
	}
}

func (b *Bookmarks) AddHandler(w http.ResponseWriter, req *http.Request) {
	q := req.URL.Query()
	name := q.Get("name")
	url := q.Get("url")
	if url == "" || name == "" {
		http.Error(w, "missing url or name: " + req.URL.String(),
			http.StatusInternalServerError)
		return
	}
	b.Add(Bookmark{
		Name: q.Get("name"),
		Url: q.Get("url"),
		Added: time.Now(),
	})

	http.Redirect(w, req, "/", http.StatusMovedPermanently)
}

func (b *Bookmarks) ListHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	b.mu.Lock()
	defer b.mu.Unlock()
	j, err := json.Marshal(b.Bookmarks)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	w.Write(j)
}
<commit_msg>Added bug note<commit_after>// Copyright 2012 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fracserv

import (
	"encoding/json"
	"encoding/gob"
	"log"
	"flag"
	"net/http"
	"os"
	"sync"
	"time"
)

var (
	bookmarkFn = flag.String("bookmarkFn", "/tmp/bookmark.db",
		"location to store bookmarked links")
)

func init() {
	b := NewBookmarks()
	http.HandleFunc("/bookmarks", func(w http.ResponseWriter, r *http.Request) {
		b.ListHandler(w, r)
	})
	http.HandleFunc("/bookmarks/add", func(w http.ResponseWriter, r *http.Request) {
		b.AddHandler(w, r)
	})
}

type Bookmark struct {
	Name string `json:"name"`
	Url string `json:"url"`
	Added time.Time `json:"added"`
}

type Bookmarks struct {
	Bookmarks []Bookmark
	addCh chan Bookmark
	mu   sync.RWMutex
}

func NewBookmarks() *Bookmarks {
	b := &Bookmarks{
		Bookmarks: make([]Bookmark, 0),
		addCh: make(chan Bookmark, 1),
	}
	go b.Loop()
	return b
}

func (b *Bookmarks) Add(bookmark Bookmark) {
	log.Print("Adding bookmark ", bookmark)
	b.addCh <- bookmark
}

func (b *Bookmarks) Load(fn string) error {
	log.Print("Loading bookmarks from ", fn)

	f, err := os.Open(fn)
	if err != nil {
		return err
	}
	defer f.Close()

	gf := gob.NewDecoder(f)
	err = gf.Decode(&b.Bookmarks)
	return err
}

func (b *Bookmarks) Save(fn string) error {
	log.Print("Saving bookmarks to ", fn)
	f, err := os.OpenFile(fn, os.O_WRONLY | os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer f.Close()

	gf := gob.NewEncoder(f)
	err = gf.Encode(b.Bookmarks)
	return err
}

func (b *Bookmarks) Loop() {
	// TODO(wathiede) if this is called from init, it won't get the path
	// specified by flag.bookmarkFn
	err := b.Load(*bookmarkFn)
	if err != nil {
		log.Print("Error loading bookmarks ", err)
	}
	for {
		select {
		case bookmark := <-b.addCh:
			b.mu.Lock()
			b.Bookmarks = append(b.Bookmarks, bookmark)
			// TODO This doesn't really scale
			err = b.Save(*bookmarkFn)
			if err != nil {
				log.Print("Error saving bookmarks ", err)
			}
			b.mu.Unlock()
		}
	}
}

func (b *Bookmarks) AddHandler(w http.ResponseWriter, req *http.Request) {
	q := req.URL.Query()
	name := q.Get("name")
	url := q.Get("url")
	if url == "" || name == "" {
		http.Error(w, "missing url or name: " + req.URL.String(),
			http.StatusInternalServerError)
		return
	}
	b.Add(Bookmark{
		Name: q.Get("name"),
		Url: q.Get("url"),
		Added: time.Now(),
	})

	http.Redirect(w, req, "/", http.StatusMovedPermanently)
}

func (b *Bookmarks) ListHandler(w http.ResponseWriter, req *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	b.mu.Lock()
	defer b.mu.Unlock()
	j, err := json.Marshal(b.Bookmarks)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	w.Write(j)
}
<|endoftext|>


<commit_before>package filesystem

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"flag"
	"os"
	"path/filepath"
	"reflect"
	"testing"
	"time"

	"github.com/adelowo/onecache"
)

var _ onecache.Store = MustNewFSStore("./")

var _ onecache.GarbageCollector = MustNewFSStore("./")

var fileCache *FSStore

func TestMain(m *testing.M) {

	fileCache = MustNewFSStore("./../cache")

	flag.Parse()
	os.Exit(m.Run())
}

func TestMustNewFSStore(t *testing.T) {

	defer func() {
		recover()
	}()

	_ = MustNewFSStore("/hh")
}

var sampleData = []byte("Lanre")

func TestFSStore_Set(t *testing.T) {

	err := fileCache.Set("name", sampleData, time.Minute*2)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFSStore_Get(t *testing.T) {

	val, err := fileCache.Get("name")

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

	if !reflect.DeepEqual(val, sampleData) {
		t.Fatalf(
			`Values are not equal.. Expected %v \n
			Got %v`, sampleData, val)
	}
}

func TestFSStore_GetUnknownKey(t *testing.T) {
	val, err := fileCache.Get("unknown")

	if err == nil {
		t.Fatal("Expected an error for a file that doesn't exist on the filesystem")
	}

	if val != nil {
		t.Fatalf("Expected a nil item to be return ... Got %v instead", val)
	}
}

func TestFSStore_GarbageCollection(t *testing.T) {

	err := fileCache.Set("xyz", []byte("Elon Musk"), onecache.EXPIRES_DEFAULT)

	if err != nil {
		t.Fatalf("An error occurred... %v", err)
	}

	data, err := fileCache.Get("xyz")

	if err != onecache.ErrCacheMiss {
		t.Fatal("Cached data is supposed to be expired")
	}

	if data != nil {
		t.Fatal("Garbage collected item is supposed to be empty")
	}
}

func TestFSStore_Flush(t *testing.T) {
	if err := fileCache.Flush(); err != nil {
		t.Fatalf("The cache directory, %s could not be flushed... %v", fileCache.baseDir, err)
	}
}

func TestFilePathForKey(t *testing.T) {

	key := "page_hits"

	b := md5.Sum([]byte(key))
	s := hex.EncodeToString(b[:])

	path := filepath.Join(fileCache.baseDir, s[0:2], s[2:4], s[4:6], s)

	if x := fileCache.filePathFor("page_hits"); path != x {
		t.Fatalf("Path differs.. Expected %s. Got %s instead", path, x)
	}
}

type mockSerializer struct {
}

func (b *mockSerializer) Serialize(i interface{}) ([]byte, error) {
	return nil, errors.New("Yup an error occurred")
}

func (b *mockSerializer) DeSerialize(data []byte, i interface{}) error {
	return errors.New("Yet another error")
}

func TestFSStore_GetFailsBecauseOfBytesMarshalling(t *testing.T) {

	fileCache.Set("test", []byte("test"), time.Second*1)

	fs, err := New(BaseDirectory("./cache"), Serializer(&mockSerializer{}))
	if err != nil {
		t.Fatal(err)
	}

	_, err = fs.Get("test")
	if err == nil {
		t.Fatalf(
			`Expected a cache miss.. Got %v`, err)
	}
}

func TestFSStore_SetFailsBecauseOfBytesMarshalling(t *testing.T) {

	fs, err := New(BaseDirectory("./cache"), Serializer(&mockSerializer{}))
	if err != nil {
		t.Fatal(err)
	}

	defer fs.Flush()

	err = fs.Set("test", []byte("test"), time.Nanosecond*4)
	if err == nil {
		t.Fatalf(
			`Expected an error from bytes marshalling.. Got %v`, err)
	}
}

func TestFSStore_Delete(t *testing.T) {
	if err := fileCache.Delete("name"); err != nil {
		t.Fatalf("Could not delete the cached data... %v", err)
	}
}

func TestFSStore_GC(t *testing.T) {
	store := MustNewFSStore("./../cache")
	defer store.Flush()

	tableTests := []struct {
		key, value string
		expires    time.Duration
	}{
		{"name", "Onecache", time.Microsecond},
		{"number", "Fourty two", time.Microsecond},
		{"x", "yz", time.Microsecond},
	}

	for _, v := range tableTests {
		store.Set(v.key, []byte(v.value), v.expires)
	}

	ticker := time.NewTicker(time.Millisecond)
	<-ticker.C

	store.GC()

	var filePath string

	for _, v := range tableTests {

		filePath = store.filePathFor(v.key)

		if _, err := os.Stat(filePath); err == nil {
			t.Fatal(
				`File exists when it isn't supposed to since there was
				a garbage collection`)
		}
	}
}

func TestFSStore_Has(t *testing.T) {
	store := MustNewFSStore("./../cache")

	defer store.Flush()

	if ok := store.Has("name"); ok {
		t.Fatalf("Key %s is not supposed to exist in the cache", "name")
	}

	store.Set("name", []byte("Lanre"), time.Hour*10)

	if ok := store.Has("name"); !ok {
		t.Fatalf(`Expected store to have an item with key %s
			since that key was persisted secs ago`, "name")
	}
}
<commit_msg>check backwards compatibility with filecache generator<commit_after>package filesystem

import (
	"crypto/md5"
	"encoding/hex"
	"errors"
	"flag"
	"os"
	"path/filepath"
	"reflect"
	"testing"
	"time"

	"github.com/adelowo/onecache"
)

var _ onecache.Store = MustNewFSStore("./")

var _ onecache.GarbageCollector = MustNewFSStore("./")

var fileCache *FSStore

func TestMain(m *testing.M) {

	fileCache = MustNewFSStore("./../cache")

	flag.Parse()
	os.Exit(m.Run())
}

func TestMustNewFSStore(t *testing.T) {

	defer func() {
		recover()
	}()

	_ = MustNewFSStore("/hh")
}

var sampleData = []byte("Lanre")

func TestFSStore_Set(t *testing.T) {

	err := fileCache.Set("name", sampleData, time.Minute*2)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFSStore_Get(t *testing.T) {

	val, err := fileCache.Get("name")

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

	if !reflect.DeepEqual(val, sampleData) {
		t.Fatalf(
			`Values are not equal.. Expected %v \n
			Got %v`, sampleData, val)
	}
}

func TestFSStore_GetUnknownKey(t *testing.T) {
	val, err := fileCache.Get("unknown")

	if err == nil {
		t.Fatal("Expected an error for a file that doesn't exist on the filesystem")
	}

	if val != nil {
		t.Fatalf("Expected a nil item to be return ... Got %v instead", val)
	}
}

func TestFSStore_GarbageCollection(t *testing.T) {

	err := fileCache.Set("xyz", []byte("Elon Musk"), onecache.EXPIRES_DEFAULT)

	if err != nil {
		t.Fatalf("An error occurred... %v", err)
	}

	data, err := fileCache.Get("xyz")

	if err != onecache.ErrCacheMiss {
		t.Fatal("Cached data is supposed to be expired")
	}

	if data != nil {
		t.Fatal("Garbage collected item is supposed to be empty")
	}
}

func TestFSStore_Flush(t *testing.T) {
	if err := fileCache.Flush(); err != nil {
		t.Fatalf("The cache directory, %s could not be flushed... %v", fileCache.baseDir, err)
	}
}

func TestFilePathForKey(t *testing.T) {

	key := "page_hits"

	b := md5.Sum([]byte(key))
	s := hex.EncodeToString(b[:])

	path := filepath.Join(fileCache.baseDir, s[0:2], s[2:4], s[4:6], s)

	if x := fileCache.filePathFor("page_hits"); path != x && FilePathKeyFunc(key) != path {
		t.Fatalf("Path differs.. Expected %s. Got %s instead", path, x)
	}
}

type mockSerializer struct {
}

func (b *mockSerializer) Serialize(i interface{}) ([]byte, error) {
	return nil, errors.New("Yup an error occurred")
}

func (b *mockSerializer) DeSerialize(data []byte, i interface{}) error {
	return errors.New("Yet another error")
}

func TestFSStore_GetFailsBecauseOfBytesMarshalling(t *testing.T) {

	fileCache.Set("test", []byte("test"), time.Second*1)

	fs, err := New(BaseDirectory("./cache"), Serializer(&mockSerializer{}))
	if err != nil {
		t.Fatal(err)
	}

	_, err = fs.Get("test")
	if err == nil {
		t.Fatalf(
			`Expected a cache miss.. Got %v`, err)
	}
}

func TestFSStore_SetFailsBecauseOfBytesMarshalling(t *testing.T) {

	fs, err := New(BaseDirectory("./cache"), Serializer(&mockSerializer{}))
	if err != nil {
		t.Fatal(err)
	}

	defer fs.Flush()

	err = fs.Set("test", []byte("test"), time.Nanosecond*4)
	if err == nil {
		t.Fatalf(
			`Expected an error from bytes marshalling.. Got %v`, err)
	}
}

func TestFSStore_Delete(t *testing.T) {
	if err := fileCache.Delete("name"); err != nil {
		t.Fatalf("Could not delete the cached data... %v", err)
	}
}

func TestFSStore_GC(t *testing.T) {
	store := MustNewFSStore("./../cache")
	defer store.Flush()

	tableTests := []struct {
		key, value string
		expires    time.Duration
	}{
		{"name", "Onecache", time.Microsecond},
		{"number", "Fourty two", time.Microsecond},
		{"x", "yz", time.Microsecond},
	}

	for _, v := range tableTests {
		store.Set(v.key, []byte(v.value), v.expires)
	}

	ticker := time.NewTicker(time.Millisecond)
	<-ticker.C

	store.GC()

	var filePath string

	for _, v := range tableTests {

		filePath = store.filePathFor(v.key)

		if _, err := os.Stat(filePath); err == nil {
			t.Fatal(
				`File exists when it isn't supposed to since there was
				a garbage collection`)
		}
	}
}

func TestFSStore_Has(t *testing.T) {
	store := MustNewFSStore("./../cache")

	defer store.Flush()

	if ok := store.Has("name"); ok {
		t.Fatalf("Key %s is not supposed to exist in the cache", "name")
	}

	store.Set("name", []byte("Lanre"), time.Hour*10)

	if ok := store.Has("name"); !ok {
		t.Fatalf(`Expected store to have an item with key %s
			since that key was persisted secs ago`, "name")
	}
}
<|endoftext|>


<commit_before>package filter_test

import (
	"github.com/pivotal-cf/go-pivnet"
	gp "github.com/pivotal-cf/go-pivnet"
	"github.com/pivotal-cf/pivnet-resource/filter"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Filter", func() {
	var (
		f *filter.Filter

		releases []pivnet.Release
	)

	BeforeEach(func() {
		f = filter.NewFilter()

		releases = []pivnet.Release{
			{
				ID:          1,
				Version:     "version1",
				ReleaseType: pivnet.ReleaseType("foo"),
			},
			{
				ID:          2,
				Version:     "version2",
				ReleaseType: pivnet.ReleaseType("bar"),
			},
			{
				ID:          3,
				Version:     "version3",
				ReleaseType: pivnet.ReleaseType("foo"),
			},
		}
	})

	Describe("ReleasesByReleaseType", func() {
		var (
			releaseType pivnet.ReleaseType
			releases    []gp.Release
		)

		BeforeEach(func() {
			releaseType = pivnet.ReleaseType("foo")

			releases = []gp.Release{
				{
					ID:          1,
					Version:     "version1",
					ReleaseType: pivnet.ReleaseType("foo"),
				},
				{
					ID:          2,
					Version:     "version2",
					ReleaseType: pivnet.ReleaseType("bar"),
				},
				{
					ID:          3,
					Version:     "version3",
					ReleaseType: pivnet.ReleaseType("foo"),
				},
			}
		})

		It("filters releases by release type without error", func() {
			filteredReleases, err := f.ReleasesByReleaseType(releases, releaseType)

			Expect(err).NotTo(HaveOccurred())

			Expect(filteredReleases).To(HaveLen(2))
			Expect(filteredReleases).To(ContainElement(releases[0]))
			Expect(filteredReleases).To(ContainElement(releases[2]))
		})

		Context("when the input releases are nil", func() {
			BeforeEach(func() {
				releases = nil
			})

			It("returns empty slice without error", func() {
				filteredReleases, err := f.ReleasesByReleaseType(releases, releaseType)

				Expect(err).NotTo(HaveOccurred())

				Expect(filteredReleases).NotTo(BeNil())
				Expect(filteredReleases).To(HaveLen(0))
			})
		})
	})

	Describe("ReleasesByVersion", func() {
		var (
			version  string
			releases []gp.Release
		)

		BeforeEach(func() {
			version = "version2"

			releases = []gp.Release{
				{
					ID:          1,
					Version:     "version1",
					ReleaseType: "foo",
				},
				{
					ID:          2,
					Version:     "version2",
					ReleaseType: "bar",
				},
				{
					ID:          3,
					Version:     "version3",
					ReleaseType: "foo",
				},
				{
					ID:          4,
					Version:     "version3.2",
					ReleaseType: "foo-minor",
				},
				{
					ID:          5,
					Version:     "version3.1.2",
					ReleaseType: "foo-patch",
				},
			}
		})

		It("filters releases by version without error", func() {
			filteredReleases, err := f.ReleasesByVersion(releases, version)

			Expect(err).NotTo(HaveOccurred())

			Expect(filteredReleases).To(HaveLen(1))
			Expect(filteredReleases).To(ContainElement(releases[1]))
		})

		Context("when the input releases are nil", func() {
			BeforeEach(func() {
				releases = nil
			})

			It("returns empty slice without error", func() {
				filteredReleases, err := f.ReleasesByVersion(releases, version)

				Expect(err).NotTo(HaveOccurred())

				Expect(filteredReleases).NotTo(BeNil())
				Expect(filteredReleases).To(HaveLen(0))
			})
		})

		Describe("Matching on regex", func() {
			Context("when the regex matches one release versions", func() {
				BeforeEach(func() {
					version = `version3\.1\..*`
				})

				It("returns all releases that match the regex without error", func() {
					filteredReleases, err := f.ReleasesByVersion(releases, version)

					Expect(err).NotTo(HaveOccurred())

					Expect(filteredReleases).To(HaveLen(1))
					Expect(filteredReleases).To(ContainElement(releases[4]))
				})
			})

			Context("when the regex matches multiple release versions", func() {
				BeforeEach(func() {
					version = `version3\..*`
				})

				It("returns all releases that match the regex without error", func() {
					filteredReleases, err := f.ReleasesByVersion(releases, version)

					Expect(err).NotTo(HaveOccurred())

					Expect(filteredReleases).To(HaveLen(2))
					Expect(filteredReleases).To(ContainElement(releases[3]))
					Expect(filteredReleases).To(ContainElement(releases[4]))
				})
			})

			Context("when the regex is invalid", func() {
				BeforeEach(func() {
					version = "some(invalid^regex"
				})

				It("returns error", func() {
					_, err := f.ReleasesByVersion(releases, version)

					Expect(err).To(HaveOccurred())
				})
			})
		})
	})

	Describe("Download Links", func() {
		It("returns the download links", func() {
			productFiles := []pivnet.ProductFile{
				{
					ID:           3,
					AWSObjectKey: "product_files/banana/file-name-1.zip",
					Links:        &pivnet.Links{Download: map[string]string{"href": "/products/banana/releases/666/product_files/6/download"}},
				},
				{
					ID:           4,
					AWSObjectKey: "product_files/banana/file-name-2.zip",
					Links:        &pivnet.Links{Download: map[string]string{"href": "/products/banana/releases/666/product_files/8/download"}},
				},
			}

			links := f.DownloadLinks(productFiles)
			Expect(links).To(HaveLen(2))
			Expect(links).To(Equal(map[string]string{
				"file-name-1.zip": "/products/banana/releases/666/product_files/6/download",
				"file-name-2.zip": "/products/banana/releases/666/product_files/8/download",
			}))
		})
	})

	Describe("Download Links by Globs", func() {
		var (
			failOnNoMatch bool
		)

		BeforeEach(func() {
			failOnNoMatch = true
		})

		It("returns the download links that match the glob filters", func() {
			downloadLinks := map[string]string{
				"android-file.zip": "/products/banana/releases/666/product_files/6/download",
				"ios-file.zip":     "/products/banana/releases/666/product_files/8/download",
				"random-file.zip":  "/products/banana/releases/666/product_files/8/download",
			}

			filteredDownloadLinks, err := f.DownloadLinksByGlobs(
				downloadLinks,
				[]string{"*android*", "*ios*"},
				failOnNoMatch,
			)

			Expect(err).NotTo(HaveOccurred())
			Expect(filteredDownloadLinks).To(HaveLen(2))
			Expect(filteredDownloadLinks).To(Equal(map[string]string{
				"android-file.zip": "/products/banana/releases/666/product_files/6/download",
				"ios-file.zip":     "/products/banana/releases/666/product_files/8/download",
			}))
		})

		Context("when a bad pattern is passed", func() {
			It("returns an error", func() {
				downloadLinks := map[string]string{
					"android-file.zip": "/products/banana/releases/666/product_files/6/download",
				}

				_, err := f.DownloadLinksByGlobs(
					downloadLinks,
					[]string{"["},
					failOnNoMatch,
				)
				Expect(err).To(HaveOccurred())
				Expect(err).To(MatchError("syntax error in pattern"))
			})
		})

		Describe("Passed a glob that matches no files", func() {
			It("returns an error", func() {
				downloadLinks := map[string]string{
					"android-file.zip": "/products/banana/releases/666/product_files/6/download",
				}

				_, err := f.DownloadLinksByGlobs(
					downloadLinks,
					[]string{"*ios*"},
					failOnNoMatch,
				)
				Expect(err).To(HaveOccurred())
				Expect(err).To(MatchError("no files match glob: *ios*"))
			})

			Context("when fail on no match is false", func() {
				BeforeEach(func() {
					failOnNoMatch = false
				})

				It("does not return an error", func() {
					downloadLinks := map[string]string{
						"android-file.zip": "/products/banana/releases/666/product_files/6/download",
					}

					_, err := f.DownloadLinksByGlobs(
						downloadLinks,
						[]string{"*ios*"},
						failOnNoMatch,
					)
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Describe("When a glob that matches a file and glob that does not match a file", func() {
			It("returns an error", func() {
				downloadLinks := map[string]string{
					"android-file.zip": "/products/banana/releases/666/product_files/6/download",
				}

				_, err := f.DownloadLinksByGlobs(
					downloadLinks,
					[]string{"android-file.zip", "does-not-exist.txt"},
					failOnNoMatch,
				)
				Expect(err).To(HaveOccurred())
				Expect(err).To(MatchError("no files match glob: does-not-exist.txt"))
			})
		})

		Context("when the glob is nil", func() {
			It("matches all files", func() {
				downloadLinks := map[string]string{
					"android-file.zip": "/products/banana/releases/666/product_files/6/download",
					"ios-file.zip":     "/products/banana/releases/666/product_files/8/download",
				}

				filteredDownloadLinks, err := f.DownloadLinksByGlobs(
					downloadLinks,
					nil,
					failOnNoMatch,
				)

				Expect(err).NotTo(HaveOccurred())
				Expect(filteredDownloadLinks).To(HaveLen(2))
				Expect(filteredDownloadLinks).To(Equal(map[string]string{
					"android-file.zip": "/products/banana/releases/666/product_files/6/download",
					"ios-file.zip":     "/products/banana/releases/666/product_files/8/download",
				}))
			})
		})
	})
})
<commit_msg>Clean up filter test.<commit_after>package filter_test

import (
	"github.com/pivotal-cf/go-pivnet"
	gp "github.com/pivotal-cf/go-pivnet"
	"github.com/pivotal-cf/pivnet-resource/filter"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Filter", func() {
	var (
		f *filter.Filter

		releases []pivnet.Release
	)

	BeforeEach(func() {
		f = filter.NewFilter()

		releases = []pivnet.Release{
			{
				ID:          1,
				Version:     "version1",
				ReleaseType: pivnet.ReleaseType("foo"),
			},
			{
				ID:          2,
				Version:     "version2",
				ReleaseType: pivnet.ReleaseType("bar"),
			},
			{
				ID:          3,
				Version:     "version3",
				ReleaseType: pivnet.ReleaseType("foo"),
			},
		}
	})

	Describe("ReleasesByReleaseType", func() {
		var (
			releaseType pivnet.ReleaseType
			releases    []gp.Release
		)

		BeforeEach(func() {
			releaseType = pivnet.ReleaseType("foo")

			releases = []gp.Release{
				{
					ID:          1,
					Version:     "version1",
					ReleaseType: pivnet.ReleaseType("foo"),
				},
				{
					ID:          2,
					Version:     "version2",
					ReleaseType: pivnet.ReleaseType("bar"),
				},
				{
					ID:          3,
					Version:     "version3",
					ReleaseType: pivnet.ReleaseType("foo"),
				},
			}
		})

		It("filters releases by release type without error", func() {
			filteredReleases, err := f.ReleasesByReleaseType(releases, releaseType)

			Expect(err).NotTo(HaveOccurred())

			Expect(filteredReleases).To(HaveLen(2))
			Expect(filteredReleases).To(ContainElement(releases[0]))
			Expect(filteredReleases).To(ContainElement(releases[2]))
		})

		Context("when the input releases are nil", func() {
			BeforeEach(func() {
				releases = nil
			})

			It("returns empty slice without error", func() {
				filteredReleases, err := f.ReleasesByReleaseType(releases, releaseType)

				Expect(err).NotTo(HaveOccurred())

				Expect(filteredReleases).NotTo(BeNil())
				Expect(filteredReleases).To(HaveLen(0))
			})
		})
	})

	Describe("ReleasesByVersion", func() {
		var (
			version  string
			releases []gp.Release
		)

		BeforeEach(func() {
			version = "version2"

			releases = []gp.Release{
				{
					ID:          1,
					Version:     "version1",
					ReleaseType: "foo",
				},
				{
					ID:          2,
					Version:     "version2",
					ReleaseType: "bar",
				},
				{
					ID:          3,
					Version:     "version3",
					ReleaseType: "foo",
				},
				{
					ID:          4,
					Version:     "version3.2",
					ReleaseType: "foo-minor",
				},
				{
					ID:          5,
					Version:     "version3.1.2",
					ReleaseType: "foo-patch",
				},
			}
		})

		It("filters releases by version without error", func() {
			filteredReleases, err := f.ReleasesByVersion(releases, version)

			Expect(err).NotTo(HaveOccurred())

			Expect(filteredReleases).To(HaveLen(1))
			Expect(filteredReleases).To(ContainElement(releases[1]))
		})

		Context("when the input releases are nil", func() {
			BeforeEach(func() {
				releases = nil
			})

			It("returns empty slice without error", func() {
				filteredReleases, err := f.ReleasesByVersion(releases, version)

				Expect(err).NotTo(HaveOccurred())

				Expect(filteredReleases).NotTo(BeNil())
				Expect(filteredReleases).To(HaveLen(0))
			})
		})

		Describe("Matching on regex", func() {
			Context("when the regex matches one release versions", func() {
				BeforeEach(func() {
					version = `version3\.1\..*`
				})

				It("returns all releases that match the regex without error", func() {
					filteredReleases, err := f.ReleasesByVersion(releases, version)

					Expect(err).NotTo(HaveOccurred())

					Expect(filteredReleases).To(HaveLen(1))
					Expect(filteredReleases).To(ContainElement(releases[4]))
				})
			})

			Context("when the regex matches multiple release versions", func() {
				BeforeEach(func() {
					version = `version3\..*`
				})

				It("returns all releases that match the regex without error", func() {
					filteredReleases, err := f.ReleasesByVersion(releases, version)

					Expect(err).NotTo(HaveOccurred())

					Expect(filteredReleases).To(HaveLen(2))
					Expect(filteredReleases).To(ContainElement(releases[3]))
					Expect(filteredReleases).To(ContainElement(releases[4]))
				})
			})

			Context("when the regex is invalid", func() {
				BeforeEach(func() {
					version = "some(invalid^regex"
				})

				It("returns error", func() {
					_, err := f.ReleasesByVersion(releases, version)

					Expect(err).To(HaveOccurred())
				})
			})
		})
	})

	Describe("Download Links", func() {
		It("returns the download links", func() {
			productFiles := []pivnet.ProductFile{
				{
					ID:           3,
					AWSObjectKey: "product_files/banana/file-name-1.zip",
					Links:        &pivnet.Links{Download: map[string]string{"href": "android-link"}},
				},
				{
					ID:           4,
					AWSObjectKey: "product_files/banana/file-name-2.zip",
					Links:        &pivnet.Links{Download: map[string]string{"href": "ios-link"}},
				},
			}

			links := f.DownloadLinks(productFiles)
			Expect(links).To(HaveLen(2))
			Expect(links).To(Equal(map[string]string{
				"file-name-1.zip": "android-link",
				"file-name-2.zip": "ios-link",
			}))
		})
	})

	Describe("Download Links by Globs", func() {
		var (
			failOnNoMatch bool
			downloadLinks map[string]string
			globs         []string
		)

		BeforeEach(func() {
			failOnNoMatch = true

			downloadLinks = map[string]string{
				"android-file.zip": "android-link",
				"ios-file.zip":     "ios-link",
				"random-file.zip":  "random-link",
			}

			globs = []string{"*android*", "*ios*"}
		})

		It("returns the download links that match the glob filters", func() {
			filteredDownloadLinks, err := f.DownloadLinksByGlobs(
				downloadLinks,
				globs,
				failOnNoMatch,
			)

			Expect(err).NotTo(HaveOccurred())
			Expect(filteredDownloadLinks).To(HaveLen(2))
			Expect(filteredDownloadLinks).To(Equal(map[string]string{
				"android-file.zip": "android-link",
				"ios-file.zip":     "ios-link",
			}))
		})

		Context("when a bad pattern is passed", func() {
			BeforeEach(func() {
				globs = []string{"["}
			})

			It("returns an error", func() {
				_, err := f.DownloadLinksByGlobs(
					downloadLinks,
					globs,
					failOnNoMatch,
				)
				Expect(err).To(HaveOccurred())
				Expect(err).To(MatchError("syntax error in pattern"))
			})
		})

		Describe("Passed a glob that matches no files", func() {
			BeforeEach(func() {
				globs = []string{"*will-not-match*"}
			})

			It("returns an error", func() {
				_, err := f.DownloadLinksByGlobs(
					downloadLinks,
					globs,
					failOnNoMatch,
				)
				Expect(err).To(HaveOccurred())
				Expect(err).To(MatchError("no files match glob: *will-not-match*"))
			})

			Context("when fail on no match is false", func() {
				BeforeEach(func() {
					failOnNoMatch = false
				})

				It("does not return an error", func() {
					_, err := f.DownloadLinksByGlobs(
						downloadLinks,
						globs,
						failOnNoMatch,
					)
					Expect(err).NotTo(HaveOccurred())
				})
			})
		})

		Describe("When a glob that matches a file and glob that does not match a file", func() {
			BeforeEach(func() {
				globs = []string{"android-file.zip", "does-not-exist.txt"}
			})

			It("returns an error", func() {
				_, err := f.DownloadLinksByGlobs(
					downloadLinks,
					globs,
					failOnNoMatch,
				)
				Expect(err).To(HaveOccurred())
				Expect(err).To(MatchError("no files match glob: does-not-exist.txt"))
			})
		})

		Context("when the glob is nil", func() {
			BeforeEach(func() {
				globs = nil
			})

			It("matches all files", func() {
				filteredDownloadLinks, err := f.DownloadLinksByGlobs(
					downloadLinks,
					nil,
					failOnNoMatch,
				)

				Expect(err).NotTo(HaveOccurred())
				Expect(filteredDownloadLinks).To(HaveLen(3))
				Expect(filteredDownloadLinks).To(Equal(map[string]string{
					"android-file.zip": "android-link",
					"ios-file.zip":     "ios-link",
					"random-file.zip":  "random-link",
				}))
			})
		})
	})
})
<|endoftext|>


<commit_before>//extend FlagSet with support for flag aliasMap
package uggo

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

type FlagSetWithAliases struct {
	*flag.FlagSet
	name           string
	argUsage       string
	out            io.Writer
	aliasMap       map[string][]string
	isPrintUsage   *bool //optional usage (you can use your own, or none, instead)
	isPrintVersion *bool //optional (you can use your own, or none, instead)
	version        string
}

func NewFlagSet(desc string, errorHandling flag.ErrorHandling) FlagSetWithAliases {
	fs := flag.NewFlagSet(desc, errorHandling)
	fs.SetOutput(ioutil.Discard)
	return FlagSetWithAliases{fs, desc, "", os.Stderr, map[string][]string{}, nil, nil, "unknown"}
}

func NewFlagSetDefault(name, argUsage, version string) FlagSetWithAliases {
	fs := flag.NewFlagSet(name+" "+argUsage, flag.ContinueOnError)
	fs.SetOutput(ioutil.Discard)
	tmp := false
	tmp2 := false
	flagSet := FlagSetWithAliases{fs, name, argUsage, os.Stderr, map[string][]string{}, &tmp, &tmp2, version}
	flagSet.BoolVar(flagSet.isPrintUsage, "help", false, "Show this help")
	flagSet.BoolVar(flagSet.isPrintVersion, "version", false, "Show version")
	flagSet.version = version
	return flagSet
}

func (flagSet FlagSetWithAliases) ProcessHelpOrVersion() bool {
	if flagSet.IsHelp() {
		flagSet.Usage()
		return true
	} else if flagSet.IsVersion() {
		flagSet.PrintVersion()
		return true
	}
	return false
}

//convenience method for storing 'usage' behaviour
func (flagSet FlagSetWithAliases) IsHelp() bool {
	return *flagSet.isPrintUsage
}

//convenience method for storing 'get version' behaviour
func (flagSet FlagSetWithAliases) IsVersion() bool {
	return *flagSet.isPrintVersion
}

func (flagSet FlagSetWithAliases) PrintVersion() {
	fmt.Fprintf(flagSet.out, "`%s` version: '%s'\n", flagSet.name, flagSet.version)
}

func (flagSet FlagSetWithAliases) Usage() {
	fmt.Fprintf(flagSet.out, "`%s %s`:\n", flagSet.name, flagSet.argUsage)
	flagSet.PrintDefaults()
}

func (flagSet FlagSetWithAliases) SetOutput(out io.Writer) {
	flagSet.out = out
}

func (flagSet FlagSetWithAliases) AliasedBoolVar(p *bool, items []string, def bool, description string) {
	flagSet.RecordAliases(items, "bool")
	for _, item := range items {
		flagSet.BoolVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedDurationVar(p *time.Duration, items []string, def time.Duration, description string) {
	flagSet.RecordAliases(items, "duration")
	for _, item := range items {
		flagSet.DurationVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedFloat64Var(p *float64, items []string, def float64, description string) {
	flagSet.RecordAliases(items, "float64")
	for _, item := range items {
		flagSet.Float64Var(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedIntVar(p *int, items []string, def int, description string) {
	flagSet.RecordAliases(items, "int")
	for _, item := range items {
		flagSet.IntVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedInt64Var(p *int64, items []string, def int64, description string) {
	flagSet.RecordAliases(items, "int64")
	for _, item := range items {
		flagSet.Int64Var(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedStringVar(p *string, items []string, def string, description string) {
	flagSet.RecordAliases(items, "string")
	for _, item := range items {
		flagSet.StringVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) isAlternative(name string) bool {
	for _, altSlice := range flagSet.aliasMap {
		for _, alt := range altSlice {
			if alt == name {
				return true
			}
		}
	}
	return false
}

func (flagSet FlagSetWithAliases) RecordAliases(items []string, typ string) {
	var key string
	for i, item := range items {
		if i == 0 {
			key = item
			if _, ok := flagSet.aliasMap[key]; !ok {
				flagSet.aliasMap[key] = []string{}
			}
		} else {
			//key is same as before
			flagSet.aliasMap[key] = append(flagSet.aliasMap[key], item)
		}
	}
}

func (flagSet FlagSetWithAliases) Parse(call []string) error {
	gnuified := Gnuify(call)
	return flagSet.FlagSet.Parse(gnuified)
}

func (flagSet FlagSetWithAliases) PrintDefaults() {
	flagSet.PrintDefaultsTo(flagSet.out)
}

func (flagSet FlagSetWithAliases) PrintDefaultsTo(out io.Writer) {
	flagSet.FlagSet.VisitAll(func(fl *flag.Flag) {
		format := "-%s=%s"
		l := 0
		alts, isAliased := flagSet.aliasMap[fl.Name]
		if isAliased {
			li, _ := fmt.Fprintf(out, "  ")
			l += li
			if len(fl.Name) > 1 {
				li, _ := fmt.Fprint(out, "-")
				l += li
			}
			//no known straightforward way to test for boolean types
			if fl.DefValue == "false" {
				li, _ = fmt.Fprintf(out, "-%s", fl.Name)
				l += li
			} else {
				li, _ = fmt.Fprintf(out, format, fl.Name, fl.DefValue)
				l += li
			}
			fmt.Fprint(out, " ")
			l += 1
			for _, alt := range alts {
				if len(alt) > 1 {
					li, _ := fmt.Fprint(out, "-")
					l += li
				}
				li, _ := fmt.Fprintf(out, "-%s ", alt)
				l += li
			}
		} else if !flagSet.isAlternative(fl.Name) {
			li, _ := fmt.Fprint(out, "  ")
			l += li
			if len(fl.Name) > 1 {
				li, _ := fmt.Fprint(out, "-")
				l += li
			}
			if fl.DefValue == "false" {
				li, _ = fmt.Fprintf(out, "-%s", fl.Name)
				l += li
			} else {
				li, _ = fmt.Fprintf(out, format, fl.Name, fl.DefValue)
				l += li
			}
		} else {
			//fmt.Fprintf(out, "alias %s\n", fl.Name)
		}
		if !flagSet.isAlternative(fl.Name) {
			for l < 25 {
				l += 1
				fmt.Fprintf(out, " ")
			}
			fmt.Fprintf(out, ": %s\n", fl.Usage)
		}

	})
	fmt.Fprintln(out, "")
}
<commit_msg>reformatting usage<commit_after>//extend FlagSet with support for flag aliasMap
package uggo

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"time"
)

type FlagSetWithAliases struct {
	*flag.FlagSet
	name           string
	argUsage       string
	out            io.Writer
	aliasMap       map[string][]string
	isPrintUsage   *bool //optional usage (you can use your own, or none, instead)
	isPrintVersion *bool //optional (you can use your own, or none, instead)
	version        string
}

func NewFlagSet(desc string, errorHandling flag.ErrorHandling) FlagSetWithAliases {
	fs := flag.NewFlagSet(desc, errorHandling)
	fs.SetOutput(ioutil.Discard)
	return FlagSetWithAliases{fs, desc, "", os.Stderr, map[string][]string{}, nil, nil, "unknown"}
}

func NewFlagSetDefault(name, argUsage, version string) FlagSetWithAliases {
	fs := flag.NewFlagSet(name+" "+argUsage, flag.ContinueOnError)
	fs.SetOutput(ioutil.Discard)
	tmp := false
	tmp2 := false
	flagSet := FlagSetWithAliases{fs, name, argUsage, os.Stderr, map[string][]string{}, &tmp, &tmp2, version}
	flagSet.BoolVar(flagSet.isPrintUsage, "help", false, "Show this help")
	flagSet.BoolVar(flagSet.isPrintVersion, "version", false, "Show version")
	flagSet.version = version
	return flagSet
}

func (flagSet FlagSetWithAliases) ProcessHelpOrVersion() bool {
	if flagSet.IsHelp() {
		flagSet.Usage()
		return true
	} else if flagSet.IsVersion() {
		flagSet.PrintVersion()
		return true
	}
	return false
}

//convenience method for storing 'usage' behaviour
func (flagSet FlagSetWithAliases) IsHelp() bool {
	return *flagSet.isPrintUsage
}

//convenience method for storing 'get version' behaviour
func (flagSet FlagSetWithAliases) IsVersion() bool {
	return *flagSet.isPrintVersion
}

func (flagSet FlagSetWithAliases) PrintVersion() {
	fmt.Fprintf(flagSet.out, "`%s` version: '%s'\n", flagSet.name, flagSet.version)
}

func (flagSet FlagSetWithAliases) Usage() {
	fmt.Fprintf(flagSet.out, "Usage: `%s %s`\n", flagSet.name, flagSet.argUsage)
	flagSet.PrintDefaults()
}

func (flagSet FlagSetWithAliases) SetOutput(out io.Writer) {
	flagSet.out = out
}

func (flagSet FlagSetWithAliases) AliasedBoolVar(p *bool, items []string, def bool, description string) {
	flagSet.RecordAliases(items, "bool")
	for _, item := range items {
		flagSet.BoolVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedDurationVar(p *time.Duration, items []string, def time.Duration, description string) {
	flagSet.RecordAliases(items, "duration")
	for _, item := range items {
		flagSet.DurationVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedFloat64Var(p *float64, items []string, def float64, description string) {
	flagSet.RecordAliases(items, "float64")
	for _, item := range items {
		flagSet.Float64Var(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedIntVar(p *int, items []string, def int, description string) {
	flagSet.RecordAliases(items, "int")
	for _, item := range items {
		flagSet.IntVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedInt64Var(p *int64, items []string, def int64, description string) {
	flagSet.RecordAliases(items, "int64")
	for _, item := range items {
		flagSet.Int64Var(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) AliasedStringVar(p *string, items []string, def string, description string) {
	flagSet.RecordAliases(items, "string")
	for _, item := range items {
		flagSet.StringVar(p, item, def, description)
	}
}

func (flagSet FlagSetWithAliases) isAlternative(name string) bool {
	for _, altSlice := range flagSet.aliasMap {
		for _, alt := range altSlice {
			if alt == name {
				return true
			}
		}
	}
	return false
}

func (flagSet FlagSetWithAliases) RecordAliases(items []string, typ string) {
	var key string
	for i, item := range items {
		if i == 0 {
			key = item
			if _, ok := flagSet.aliasMap[key]; !ok {
				flagSet.aliasMap[key] = []string{}
			}
		} else {
			//key is same as before
			flagSet.aliasMap[key] = append(flagSet.aliasMap[key], item)
		}
	}
}

func (flagSet FlagSetWithAliases) Parse(call []string) error {
	gnuified := Gnuify(call)
	return flagSet.FlagSet.Parse(gnuified)
}

func (flagSet FlagSetWithAliases) PrintDefaults() {
	flagSet.PrintDefaultsTo(flagSet.out)
}

func (flagSet FlagSetWithAliases) PrintDefaultsTo(out io.Writer) {
	flagSet.FlagSet.VisitAll(func(fl *flag.Flag) {
		format := "-%s=%s"
		l := 0
		alts, isAliased := flagSet.aliasMap[fl.Name]
		if isAliased {
			li, _ := fmt.Fprintf(out, "  ")
			l += li
			if len(fl.Name) > 1 {
				li, _ := fmt.Fprint(out, "-")
				l += li
			}
			//no known straightforward way to test for boolean types
			if fl.DefValue == "false" {
				li, _ = fmt.Fprintf(out, "-%s", fl.Name)
				l += li
			} else {
				li, _ = fmt.Fprintf(out, format, fl.Name, fl.DefValue)
				l += li
			}
			fmt.Fprint(out, " ")
			l += 1
			for _, alt := range alts {
				if len(alt) > 1 {
					li, _ := fmt.Fprint(out, "-")
					l += li
				}
				li, _ := fmt.Fprintf(out, "-%s ", alt)
				l += li
			}
		} else if !flagSet.isAlternative(fl.Name) {
			li, _ := fmt.Fprint(out, "  ")
			l += li
			if len(fl.Name) > 1 {
				li, _ := fmt.Fprint(out, "-")
				l += li
			}
			if fl.DefValue == "false" {
				li, _ = fmt.Fprintf(out, "-%s", fl.Name)
				l += li
			} else {
				li, _ = fmt.Fprintf(out, format, fl.Name, fl.DefValue)
				l += li
			}
		} else {
			//fmt.Fprintf(out, "alias %s\n", fl.Name)
		}
		if !flagSet.isAlternative(fl.Name) {
			for l < 25 {
				l += 1
				fmt.Fprintf(out, " ")
			}
			fmt.Fprintf(out, ": %s\n", fl.Usage)
		}

	})
	fmt.Fprintln(out, "")
}
<|endoftext|>


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

package inode_test

import (
	"fmt"
	"math"
	"os"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/googlecloudplatform/gcsfuse/gcsproxy"
	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsfake"
	"github.com/jacobsa/gcloud/gcs/gcsutil"
	. "github.com/jacobsa/ogletest"
)

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

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

const uid = 123
const gid = 456

const fileInodeID = 17
const fileInodeName = "foo/bar"
const fileMode os.FileMode = 0641

type FileTest struct {
	ctx    context.Context
	bucket gcs.Bucket
	leaser lease.FileLeaser
	clock  timeutil.SimulatedClock

	initialContents string
	backingObj      *gcs.Object

	in *inode.FileInode
}

var _ SetUpInterface = &FileTest{}
var _ TearDownInterface = &FileTest{}

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

func (t *FileTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
	t.clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))
	t.leaser = lease.NewFileLeaser("", math.MaxInt32, math.MaxInt64)
	t.bucket = gcsfake.NewFakeBucket(&t.clock, "some_bucket")

	// Set up the backing object.
	var err error

	t.initialContents = "taco"
	t.backingObj, err = gcsutil.CreateObject(
		t.ctx,
		t.bucket,
		fileInodeName,
		t.initialContents)

	AssertEq(nil, err)

	// Create the inode.
	t.in = inode.NewFileInode(
		fileInodeID,
		t.backingObj,
		fuseops.InodeAttributes{
			Uid:  uid,
			Gid:  gid,
			Mode: fileMode,
		},
		math.MaxUint64, // GCS chunk size
		t.bucket,
		t.leaser,
		gcsproxy.NewObjectSyncer(
			1, // Append threshold
			".gcsfuse_tmp/",
			t.bucket),
		&t.clock)

	t.in.Lock()
}

func (t *FileTest) TearDown() {
	t.in.Unlock()
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *FileTest) ID() {
	ExpectEq(fileInodeID, t.in.ID())
}

func (t *FileTest) Name() {
	ExpectEq(fileInodeName, t.in.Name())
}

func (t *FileTest) InitialSourceGeneration() {
	ExpectEq(t.backingObj.Generation, t.in.SourceGeneration())
}

func (t *FileTest) InitialAttributes() {
	attrs, err := t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len(t.initialContents), attrs.Size)
	ExpectEq(1, attrs.Nlink)
	ExpectEq(uid, attrs.Uid)
	ExpectEq(gid, attrs.Gid)
	ExpectEq(fileMode, attrs.Mode)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(t.backingObj.Updated))
}

func (t *FileTest) Read() {
	AssertEq("taco", t.initialContents)

	// Make several reads, checking the expected contents. We should never get an
	// EOF error, since fuseops.ReadFileOp is not supposed to see those.
	testCases := []struct {
		offset   int64
		size     int
		expected string
	}{
		{0, 1, "t"},
		{0, 2, "ta"},
		{0, 3, "tac"},
		{0, 4, "taco"},
		{0, 5, "taco"},

		{1, 1, "a"},
		{1, 2, "ac"},
		{1, 3, "aco"},
		{1, 4, "aco"},

		{3, 1, "o"},
		{3, 2, "o"},

		// Empty ranges
		{0, 0, ""},
		{3, 0, ""},
		{4, 0, ""},
		{4, 1, ""},
		{5, 0, ""},
		{5, 1, ""},
		{5, 2, ""},
	}

	for _, tc := range testCases {
		desc := fmt.Sprintf("offset: %d, size: %d", tc.offset, tc.size)

		data, err := t.in.Read(t.ctx, tc.offset, tc.size)
		AssertEq(nil, err, "%s", desc)
		ExpectEq(tc.expected, string(data), "%s", desc)
	}
}

func (t *FileTest) Write() {
	var data []byte
	var err error

	AssertEq("taco", t.initialContents)

	// Overwite a byte.
	err = t.in.Write(t.ctx, []byte("p"), 0)
	AssertEq(nil, err)

	// Add some data at the end.
	t.clock.AdvanceTime(time.Second)
	writeTime := t.clock.Now()

	err = t.in.Write(t.ctx, []byte("burrito"), 4)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Read back the content.
	data, err = t.in.Read(t.ctx, 0, 1024)
	AssertEq(nil, err)
	ExpectEq("pacoburrito", string(data))

	// Check attributes.
	attrs, err := t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len("pacoburrito"), attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(writeTime))
}

func (t *FileTest) Truncate() {
	var attrs fuseops.InodeAttributes
	var data []byte
	var err error

	AssertEq("taco", t.initialContents)

	// Truncate downward.
	t.clock.AdvanceTime(time.Second)
	truncateTime := t.clock.Now()

	err = t.in.Truncate(t.ctx, 2)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Read the contents.
	data, err = t.in.Read(t.ctx, 0, 1024)
	AssertEq(nil, err)
	ExpectEq("ta", string(data))

	// Check attributes.
	attrs, err = t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len("ta"), attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(truncateTime))
}

func (t *FileTest) WriteThenSync() {
	AssertTrue(false, "TODO")
}

func (t *FileTest) AppendThenSync() {
	AssertTrue(false, "TODO")
}

func (t *FileTest) TruncateDownwardThenSync() {
	var attrs fuseops.InodeAttributes
	var err error

	// Truncate downward.
	t.clock.AdvanceTime(time.Second)

	err = t.in.Truncate(t.ctx, 2)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Sync.
	err = t.in.Sync(t.ctx)
	AssertEq(nil, err)

	// The generation should have advanced.
	ExpectLt(t.backingObj.Generation, t.in.SourceGeneration())

	// Stat the current object in the bucket.
	statReq := &gcs.StatObjectRequest{Name: t.in.Name()}
	o, err := t.bucket.StatObject(t.ctx, statReq)

	AssertEq(nil, err)
	ExpectEq(t.in.SourceGeneration(), o.Generation)
	ExpectEq(2, o.Size)

	// Check attributes.
	attrs, err = t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(2, attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(o.Updated))
}

func (t *FileTest) TruncateUpwardThenSync() {
	AssertTrue(false, "TODO")
}

func (t *FileTest) Sync_Clobbered() {
	var err error

	// Truncate downward.
	err = t.in.Truncate(t.ctx, 2)
	AssertEq(nil, err)

	// Clobber the backing object.
	newObj, err := gcsutil.CreateObject(t.ctx, t.bucket, t.in.Name(), "burrito")
	AssertEq(nil, err)

	// Sync. The call should succeed, but nothing should change.
	err = t.in.Sync(t.ctx)

	AssertEq(nil, err)
	ExpectEq(t.backingObj.Generation, t.in.SourceGeneration())

	// The object in the bucket should not have been changed.
	statReq := &gcs.StatObjectRequest{Name: t.in.Name()}
	o, err := t.bucket.StatObject(t.ctx, statReq)

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

package inode_test

import (
	"fmt"
	"math"
	"os"
	"testing"
	"time"

	"golang.org/x/net/context"

	"github.com/googlecloudplatform/gcsfuse/fs/inode"
	"github.com/googlecloudplatform/gcsfuse/gcsproxy"
	"github.com/googlecloudplatform/gcsfuse/lease"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse/fuseops"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsfake"
	"github.com/jacobsa/gcloud/gcs/gcsutil"
	. "github.com/jacobsa/ogletest"
)

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

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

const uid = 123
const gid = 456

const fileInodeID = 17
const fileInodeName = "foo/bar"
const fileMode os.FileMode = 0641

type FileTest struct {
	ctx    context.Context
	bucket gcs.Bucket
	leaser lease.FileLeaser
	clock  timeutil.SimulatedClock

	initialContents string
	backingObj      *gcs.Object

	in *inode.FileInode
}

var _ SetUpInterface = &FileTest{}
var _ TearDownInterface = &FileTest{}

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

func (t *FileTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx
	t.clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))
	t.leaser = lease.NewFileLeaser("", math.MaxInt32, math.MaxInt64)
	t.bucket = gcsfake.NewFakeBucket(&t.clock, "some_bucket")

	// Set up the backing object.
	var err error

	t.initialContents = "taco"
	t.backingObj, err = gcsutil.CreateObject(
		t.ctx,
		t.bucket,
		fileInodeName,
		t.initialContents)

	AssertEq(nil, err)

	// Create the inode.
	t.in = inode.NewFileInode(
		fileInodeID,
		t.backingObj,
		fuseops.InodeAttributes{
			Uid:  uid,
			Gid:  gid,
			Mode: fileMode,
		},
		math.MaxUint64, // GCS chunk size
		t.bucket,
		t.leaser,
		gcsproxy.NewObjectSyncer(
			1, // Append threshold
			".gcsfuse_tmp/",
			t.bucket),
		&t.clock)

	t.in.Lock()
}

func (t *FileTest) TearDown() {
	t.in.Unlock()
}

////////////////////////////////////////////////////////////////////////
// Tests
////////////////////////////////////////////////////////////////////////

func (t *FileTest) ID() {
	ExpectEq(fileInodeID, t.in.ID())
}

func (t *FileTest) Name() {
	ExpectEq(fileInodeName, t.in.Name())
}

func (t *FileTest) InitialSourceGeneration() {
	ExpectEq(t.backingObj.Generation, t.in.SourceGeneration())
}

func (t *FileTest) InitialAttributes() {
	attrs, err := t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len(t.initialContents), attrs.Size)
	ExpectEq(1, attrs.Nlink)
	ExpectEq(uid, attrs.Uid)
	ExpectEq(gid, attrs.Gid)
	ExpectEq(fileMode, attrs.Mode)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(t.backingObj.Updated))
}

func (t *FileTest) Read() {
	AssertEq("taco", t.initialContents)

	// Make several reads, checking the expected contents. We should never get an
	// EOF error, since fuseops.ReadFileOp is not supposed to see those.
	testCases := []struct {
		offset   int64
		size     int
		expected string
	}{
		{0, 1, "t"},
		{0, 2, "ta"},
		{0, 3, "tac"},
		{0, 4, "taco"},
		{0, 5, "taco"},

		{1, 1, "a"},
		{1, 2, "ac"},
		{1, 3, "aco"},
		{1, 4, "aco"},

		{3, 1, "o"},
		{3, 2, "o"},

		// Empty ranges
		{0, 0, ""},
		{3, 0, ""},
		{4, 0, ""},
		{4, 1, ""},
		{5, 0, ""},
		{5, 1, ""},
		{5, 2, ""},
	}

	for _, tc := range testCases {
		desc := fmt.Sprintf("offset: %d, size: %d", tc.offset, tc.size)

		data, err := t.in.Read(t.ctx, tc.offset, tc.size)
		AssertEq(nil, err, "%s", desc)
		ExpectEq(tc.expected, string(data), "%s", desc)
	}
}

func (t *FileTest) Write() {
	var data []byte
	var err error

	AssertEq("taco", t.initialContents)

	// Overwite a byte.
	err = t.in.Write(t.ctx, []byte("p"), 0)
	AssertEq(nil, err)

	// Add some data at the end.
	t.clock.AdvanceTime(time.Second)
	writeTime := t.clock.Now()

	err = t.in.Write(t.ctx, []byte("burrito"), 4)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Read back the content.
	data, err = t.in.Read(t.ctx, 0, 1024)
	AssertEq(nil, err)
	ExpectEq("pacoburrito", string(data))

	// Check attributes.
	attrs, err := t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len("pacoburrito"), attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(writeTime))
}

func (t *FileTest) Truncate() {
	var attrs fuseops.InodeAttributes
	var data []byte
	var err error

	AssertEq("taco", t.initialContents)

	// Truncate downward.
	t.clock.AdvanceTime(time.Second)
	truncateTime := t.clock.Now()

	err = t.in.Truncate(t.ctx, 2)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Read the contents.
	data, err = t.in.Read(t.ctx, 0, 1024)
	AssertEq(nil, err)
	ExpectEq("ta", string(data))

	// Check attributes.
	attrs, err = t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len("ta"), attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(truncateTime))
}

func (t *FileTest) WriteThenSync() {
	var attrs fuseops.InodeAttributes
	var err error

	AssertEq("taco", t.initialContents)

	// Overwite a byte.
	err = t.in.Write(t.ctx, []byte("p"), 0)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Sync.
	err = t.in.Sync(t.ctx)
	AssertEq(nil, err)

	// The generation should have advanced.
	ExpectLt(t.backingObj.Generation, t.in.SourceGeneration())

	// Stat the current object in the bucket.
	statReq := &gcs.StatObjectRequest{Name: t.in.Name()}
	o, err := t.bucket.StatObject(t.ctx, statReq)

	AssertEq(nil, err)
	ExpectEq(t.in.SourceGeneration(), o.Generation)
	ExpectEq(len("paco"), o.Size)

	// Read the object's contents.
	contents, err := gcsutil.ReadObject(t.ctx, t.bucket, t.in.Name())

	AssertEq(nil, err)
	ExpectEq("paco", string(contents))

	// Check attributes.
	attrs, err = t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(len("paco"), attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(o.Updated))
}

func (t *FileTest) AppendThenSync() {
	AssertTrue(false, "TODO")
}

func (t *FileTest) TruncateDownwardThenSync() {
	var attrs fuseops.InodeAttributes
	var err error

	// Truncate downward.
	err = t.in.Truncate(t.ctx, 2)
	AssertEq(nil, err)

	t.clock.AdvanceTime(time.Second)

	// Sync.
	err = t.in.Sync(t.ctx)
	AssertEq(nil, err)

	// The generation should have advanced.
	ExpectLt(t.backingObj.Generation, t.in.SourceGeneration())

	// Stat the current object in the bucket.
	statReq := &gcs.StatObjectRequest{Name: t.in.Name()}
	o, err := t.bucket.StatObject(t.ctx, statReq)

	AssertEq(nil, err)
	ExpectEq(t.in.SourceGeneration(), o.Generation)
	ExpectEq(2, o.Size)

	// Check attributes.
	attrs, err = t.in.Attributes(t.ctx)
	AssertEq(nil, err)

	ExpectEq(2, attrs.Size)
	ExpectThat(attrs.Mtime, timeutil.TimeEq(o.Updated))
}

func (t *FileTest) TruncateUpwardThenSync() {
	AssertTrue(false, "TODO")
}

func (t *FileTest) Sync_Clobbered() {
	var err error

	// Truncate downward.
	err = t.in.Truncate(t.ctx, 2)
	AssertEq(nil, err)

	// Clobber the backing object.
	newObj, err := gcsutil.CreateObject(t.ctx, t.bucket, t.in.Name(), "burrito")
	AssertEq(nil, err)

	// Sync. The call should succeed, but nothing should change.
	err = t.in.Sync(t.ctx)

	AssertEq(nil, err)
	ExpectEq(t.backingObj.Generation, t.in.SourceGeneration())

	// The object in the bucket should not have been changed.
	statReq := &gcs.StatObjectRequest{Name: t.in.Name()}
	o, err := t.bucket.StatObject(t.ctx, statReq)

	AssertEq(nil, err)
	ExpectEq(newObj.Generation, o.Generation)
	ExpectEq(newObj.Size, o.Size)
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 单进程高速缓存.
package gcache

const (
    // 当数据不过期时，默认设置的过期属性值，相当于：math.MaxInt64/1000000
    gDEFAULT_MAX_EXPIRE = 9223372036854
)

// 全局缓存管理对象
//var cache = New()
var cache = (*Cache)(nil)

// (使用全局KV缓存对象)设置kv缓存键值对，过期时间单位为**毫秒**
func Set(key interface{}, value interface{}, expire int)  {
    cache.Set(key, value, expire)
}

// 当键名不存在时写入，并返回true；否则返回false。
// 常用来做对并发性要求不高的内存锁。
func SetIfNotExist(key interface{}, value interface{}, expire int) bool {
    return cache.SetIfNotExist(key, value, expire)
}

// (使用全局KV缓存对象)批量设置kv缓存键值对，过期时间单位为**毫秒**
func BatchSet(data map[interface{}]interface{}, expire int)  {
    cache.BatchSet(data, expire)
}

// (使用全局KV缓存对象)获取指定键名的值
func Get(key interface{}) interface{} {
    return cache.Get(key)
}

// 当键名存在时返回其键值，否则写入指定的键值
func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
    return cache.GetOrSet(key, value, expire)
}

// 当键名存在时返回其键值，否则写入指定的键值，键值由指定的函数生成
func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
    return cache.GetOrSetFunc(key, f, expire)
}

// 是否存在指定的键名，true表示存在，false表示不存在。
func Contains(key interface{}) bool {
    return cache.Contains(key)
}

// (使用全局KV缓存对象)删除指定键值对
func Remove(key interface{}) interface{} {
    return cache.Remove(key)
}

// (使用全局KV缓存对象)批量删除指定键值对
func BatchRemove(keys []interface{}) map[interface{}]interface{} {
    return cache.BatchRemove(keys)
}

// 获得所有的键名，组成数组返回
func Keys() []interface{} {
    return cache.Keys()
}

// 获得所有的键名，组成字符串数组返回
func KeyStrings() []string {
    return cache.KeyStrings()
}

// 获得所有的值，组成数组返回
func Values() []interface{} {
    return cache.Values()
}

// 获得缓存对象的键值对数量
func Size() int {
    return cache.Size()
}
<commit_msg>去掉调试信息<commit_after>// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.

// 单进程高速缓存.
package gcache

const (
    // 当数据不过期时，默认设置的过期属性值，相当于：math.MaxInt64/1000000
    gDEFAULT_MAX_EXPIRE = 9223372036854
)

// 全局缓存管理对象
var cache = New()

// (使用全局KV缓存对象)设置kv缓存键值对，过期时间单位为**毫秒**
func Set(key interface{}, value interface{}, expire int)  {
    cache.Set(key, value, expire)
}

// 当键名不存在时写入，并返回true；否则返回false。
// 常用来做对并发性要求不高的内存锁。
func SetIfNotExist(key interface{}, value interface{}, expire int) bool {
    return cache.SetIfNotExist(key, value, expire)
}

// (使用全局KV缓存对象)批量设置kv缓存键值对，过期时间单位为**毫秒**
func BatchSet(data map[interface{}]interface{}, expire int)  {
    cache.BatchSet(data, expire)
}

// (使用全局KV缓存对象)获取指定键名的值
func Get(key interface{}) interface{} {
    return cache.Get(key)
}

// 当键名存在时返回其键值，否则写入指定的键值
func GetOrSet(key interface{}, value interface{}, expire int) interface{} {
    return cache.GetOrSet(key, value, expire)
}

// 当键名存在时返回其键值，否则写入指定的键值，键值由指定的函数生成
func GetOrSetFunc(key interface{}, f func() interface{}, expire int) interface{} {
    return cache.GetOrSetFunc(key, f, expire)
}

// 是否存在指定的键名，true表示存在，false表示不存在。
func Contains(key interface{}) bool {
    return cache.Contains(key)
}

// (使用全局KV缓存对象)删除指定键值对
func Remove(key interface{}) interface{} {
    return cache.Remove(key)
}

// (使用全局KV缓存对象)批量删除指定键值对
func BatchRemove(keys []interface{}) map[interface{}]interface{} {
    return cache.BatchRemove(keys)
}

// 获得所有的键名，组成数组返回
func Keys() []interface{} {
    return cache.Keys()
}

// 获得所有的键名，组成字符串数组返回
func KeyStrings() []string {
    return cache.KeyStrings()
}

// 获得所有的值，组成数组返回
func Values() []interface{} {
    return cache.Values()
}

// 获得缓存对象的键值对数量
func Size() int {
    return cache.Size()
}
<|endoftext|>


<commit_before>/* Copyright (C) 2015  Adam Kurkiewicz and Iva Babukova
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package main

import "fmt"
import "math/big"
import "encoding/hex"

func main() {
	//128 64 32 16 8 4 2 1
	//1   0  1  1  1 1 0 1
	//1 + 4 + 8 + 16 + 32 + 128 = 189
	slica := []byte("\xbd\xbd")
	fmt.Println(len(slica))
	bigInta := (&big.Int{}).SetBytes(slica)
	stringa := hex.EncodeToString(bigInta.Bytes())
	fmt.Println(stringa)
}
<commit_msg>added reasonable main<commit_after>/* Copyright (C) 2015  Adam Kurkiewicz and Iva Babukova
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published
 *   by the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package main
import "../../boardLib"
import "fmt"

func main() {
	move := boardLib.CreateMove()
	move.InitFirstMove()
	fmt.Println(move.InternalView())
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsfake

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"sort"
	"strings"
	"unicode/utf8"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

// Create an in-memory bucket with the given name and empty contents.
func NewFakeBucket(name string) gcs.Bucket {
	b := &bucket{name: name}
	b.mu = syncutil.NewInvariantMutex(func() { b.checkInvariants() })
	return b
}

type object struct {
	// A storage.Object representing metadata for this object. Never changes.
	metadata *storage.Object

	// The contents of the object. These never change.
	contents string
}

// A slice of objects compared by name.
type objectSlice []object

func (s objectSlice) Len() int           { return len(s) }
func (s objectSlice) Less(i, j int) bool { return s[i].metadata.Name < s[j].metadata.Name }
func (s objectSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// Return the smallest i such that s[i].metadata.Name >= name, or len(s) if
// there is no such i.
func (s objectSlice) lowerBound(name string) int {
	pred := func(i int) bool {
		return s[i].metadata.Name >= name
	}

	return sort.Search(len(s), pred)
}

// Return the smallest i such that s[i].metadata.Name == name, or len(s) if
// there is no such i.
func (s objectSlice) find(name string) int {
	lb := s.lowerBound(name)
	if lb < len(s) && s[lb].metadata.Name == name {
		return lb
	}

	return len(s)
}

// Return the smallest string that is lexicographically larger than prefix and
// does not have prefix as a prefix. For the sole case where this is not
// possible (all strings consisting solely of 0xff bytes, including the empty
// string), return the empty string.
func prefixSuccessor(prefix string) string {
	// Attempt to increment the last byte. If that is a 0xff byte, erase it and
	// recurse. If we hit an empty string, then we know our task is impossible.
	limit := []byte(prefix)
	for len(limit) > 0 {
		b := limit[len(limit)-1]
		if b != 0xff {
			limit[len(limit)-1]++
			break
		}

		limit = limit[:len(limit)-1]
	}

	return string(limit)
}

// Return the smallest i such that prefix < s[i].metadata.Name and
// !strings.HasPrefix(s[i].metadata.Name, prefix).
func (s objectSlice) prefixUpperBound(prefix string) int {
	successor := prefixSuccessor(prefix)
	if successor == "" {
		return len(s)
	}

	return s.lowerBound(successor)
}

type bucket struct {
	name string
	mu   syncutil.InvariantMutex

	// The set of extant objects.
	//
	// INVARIANT: Strictly increasing.
	objects objectSlice // GUARDED_BY(mu)
}

// SHARED_LOCKS_REQUIRED(b.mu)
func (b *bucket) checkInvariants() {
	// Make sure 'objects' is strictly increasing.
	for i := 1; i < len(b.objects); i++ {
		objA := b.objects[i-1]
		objB := b.objects[i]
		if !(objA.metadata.Name < objB.metadata.Name) {
			panic(
				fmt.Sprintf(
					"Object names are not strictly increasing: %v vs. %v",
					objA.metadata.Name,
					objB.metadata.Name))
		}
	}
}

func (b *bucket) Name() string {
	return b.name
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) ListObjects(
	ctx context.Context,
	query *storage.Query) (listing *storage.Objects, err error) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	// Set up the result object.
	listing = new(storage.Objects)

	// Handle nil queries.
	if query == nil {
		query = &storage.Query{}
	}

	// Handle defaults.
	maxResults := query.MaxResults
	if maxResults == 0 {
		maxResults = 1000
	}

	// Find where in the space of object names to start.
	nameStart := query.Prefix
	if query.Cursor != "" && query.Cursor > nameStart {
		nameStart = query.Cursor
	}

	// Find the range of indexes within the array to scan.
	indexStart := b.objects.lowerBound(nameStart)
	prefixLimit := b.objects.prefixUpperBound(query.Prefix)
	indexLimit := minInt(indexStart+maxResults, prefixLimit)

	// Scan the array.
	var lastResultWasPrefix bool
	for i := indexStart; i < indexLimit; i++ {
		var o object = b.objects[i]
		name := o.metadata.Name

		// Search for a delimiter if necessary.
		if query.Delimiter != "" {
			// Search only in the part after the prefix.
			nameMinusQueryPrefix := name[len(query.Prefix):]

			delimiterIndex := strings.Index(nameMinusQueryPrefix, query.Delimiter)
			if delimiterIndex >= 0 {
				resultPrefixLimit := delimiterIndex

				// Transform to an index within name.
				resultPrefixLimit += len(query.Prefix)

				// Include the delimiter in the result.
				resultPrefixLimit += len(query.Delimiter)

				// Save the result, but only if it's not a duplicate.
				resultPrefix := name[:resultPrefixLimit]
				if len(listing.Prefixes) == 0 ||
					listing.Prefixes[len(listing.Prefixes)-1] != resultPrefix {
					listing.Prefixes = append(listing.Prefixes, resultPrefix)
				}

				lastResultWasPrefix = true
				continue
			}
		}

		lastResultWasPrefix = false

		// Otherwise, save as an object result.
		listing.Results = append(listing.Results, o.metadata)
	}

	// Set up a cursor for where to start the next scan if we didn't exhaust the
	// results.
	if indexLimit < prefixLimit {
		listing.Next = &storage.Query{}
		*listing.Next = *query

		// Ion is if the final object we visited was returned as an element in
		// listing.Prefixes, we want to skip all other objects that would result in
		// the same so we don't return duplicate elements in listing.Prefixes
		// accross requests.
		if lastResultWasPrefix {
			lastResultPrefix := listing.Prefixes[len(listing.Prefixes)-1]
			listing.Next.Cursor = prefixSuccessor(lastResultPrefix)

			// Check an assumption: prefixSuccessor cannot result in the empty string
			// above because object names must be non-empty UTF-8 strings, and there
			// is no valid non-empty UTF-8 string that consists of entirely 0xff
			// bytes.
			if listing.Next.Cursor == "" {
				err = errors.New("Unexpected empty string from prefixSuccessor")
				return
			}
		} else {
			// Otherwise, we'll start scanning at the next object.
			listing.Next.Cursor = b.objects[indexLimit].metadata.Name
		}
	}

	return
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) NewReader(
	ctx context.Context,
	objectName string) (io.ReadCloser, error) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	index := b.objects.find(objectName)
	if index == len(b.objects) {
		return nil, errors.New("object doesn't exist.")
	}

	return ioutil.NopCloser(strings.NewReader(b.objects[index].contents)), nil
}

func (b *bucket) NewWriter(
	ctx context.Context,
	attrs *storage.ObjectAttrs) (gcs.ObjectWriter, error) {
	// Check that the object name is legal.
	name := attrs.Name
	if len(name) == 0 || len(name) > 1024 {
		return nil, errors.New("Invalid object name: length must be in [1, 1024]")
	}

	if !utf8.ValidString(name) {
		return nil, errors.New("Invalid object name: not valid UTF-8")
	}

	for _, r := range name {
		if r == 0x0a || r == 0x0d {
			return nil, errors.New("Invalid object name: must not contain CR or LF")
		}
	}

	return newObjectWriter(b, attrs), nil
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) DeleteObject(
	ctx context.Context,
	name string) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Do we possess the object with the given name?
	index := b.objects.find(name)
	if index == len(b.objects) {
		return errors.New("Object Not Found.")
	}

	// Remove the object.
	b.objects = append(b.objects[:index], b.objects[index+1:]...)

	return nil
}

// Create an object struct for the given attributes and contents.
//
// EXCLUSIVE_LOCKS_REQUIRED(b.mu)
func (b *bucket) mintObject(
	attrs *storage.ObjectAttrs,
	contents string) (o object) {
	// Set up metadata.
	// TODO(jacobsa): Other fields.
	o.metadata = &storage.Object{
		Bucket:   b.Name(),
		Name:     attrs.Name,
		Owner:    "user-fake",
		Size:     int64(len(contents)),
		Metadata: attrs.Metadata,
	}

	// Set up contents.
	o.contents = contents

	return
}

// Add a record for an object with the given attributes and contents, then
// return the minted metadata.
//
// LOCKS_EXCLUDED(b.mu)
func (b *bucket) addObject(
	attrs *storage.ObjectAttrs,
	contents string) *storage.Object {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Create an object record from the given attributes.
	var o object = b.mintObject(attrs, contents)

	// Replace an entry in or add an entry to our list of objects.
	existingIndex := b.objects.find(attrs.Name)
	if existingIndex < len(b.objects) {
		b.objects[existingIndex] = o
	} else {
		b.objects = append(b.objects, o)
		sort.Sort(b.objects)
	}

	return o.metadata
}

func minInt(a, b int) int {
	if a < b {
		return a
	}

	return b
}

func maxInt(a, b int) int {
	if a > b {
		return a
	}

	return b
}
<commit_msg>Fill in lots more metadata.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package gcsfake

import (
	"crypto/md5"
	"errors"
	"fmt"
	"hash/crc32"
	"io"
	"io/ioutil"
	"sort"
	"strings"
	"unicode/utf8"

	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
	"google.golang.org/cloud/storage"
)

var crc32Table = crc32.MakeTable(crc32.Castagnoli)

// Create an in-memory bucket with the given name and empty contents.
func NewFakeBucket(name string) gcs.Bucket {
	b := &bucket{name: name}
	b.mu = syncutil.NewInvariantMutex(func() { b.checkInvariants() })
	return b
}

type object struct {
	// A storage.Object representing metadata for this object. Never changes.
	metadata *storage.Object

	// The contents of the object. These never change.
	contents string
}

// A slice of objects compared by name.
type objectSlice []object

func (s objectSlice) Len() int           { return len(s) }
func (s objectSlice) Less(i, j int) bool { return s[i].metadata.Name < s[j].metadata.Name }
func (s objectSlice) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

// Return the smallest i such that s[i].metadata.Name >= name, or len(s) if
// there is no such i.
func (s objectSlice) lowerBound(name string) int {
	pred := func(i int) bool {
		return s[i].metadata.Name >= name
	}

	return sort.Search(len(s), pred)
}

// Return the smallest i such that s[i].metadata.Name == name, or len(s) if
// there is no such i.
func (s objectSlice) find(name string) int {
	lb := s.lowerBound(name)
	if lb < len(s) && s[lb].metadata.Name == name {
		return lb
	}

	return len(s)
}

// Return the smallest string that is lexicographically larger than prefix and
// does not have prefix as a prefix. For the sole case where this is not
// possible (all strings consisting solely of 0xff bytes, including the empty
// string), return the empty string.
func prefixSuccessor(prefix string) string {
	// Attempt to increment the last byte. If that is a 0xff byte, erase it and
	// recurse. If we hit an empty string, then we know our task is impossible.
	limit := []byte(prefix)
	for len(limit) > 0 {
		b := limit[len(limit)-1]
		if b != 0xff {
			limit[len(limit)-1]++
			break
		}

		limit = limit[:len(limit)-1]
	}

	return string(limit)
}

// Return the smallest i such that prefix < s[i].metadata.Name and
// !strings.HasPrefix(s[i].metadata.Name, prefix).
func (s objectSlice) prefixUpperBound(prefix string) int {
	successor := prefixSuccessor(prefix)
	if successor == "" {
		return len(s)
	}

	return s.lowerBound(successor)
}

type bucket struct {
	name string
	mu   syncutil.InvariantMutex

	// The set of extant objects.
	//
	// INVARIANT: Strictly increasing.
	objects objectSlice // GUARDED_BY(mu)

	// The most recent generation number that was minted. The next object will
	// receive generation prevGeneration + 1.
	//
	// INVARIANT: This is an upper bound for generation numbers in objectSlice.
	prevGeneration int64 // GUARDED_BY(mu)
}

// SHARED_LOCKS_REQUIRED(b.mu)
func (b *bucket) checkInvariants() {
	// Make sure 'objects' is strictly increasing.
	for i := 1; i < len(b.objects); i++ {
		objA := b.objects[i-1]
		objB := b.objects[i]
		if !(objA.metadata.Name < objB.metadata.Name) {
			panic(
				fmt.Sprintf(
					"Object names are not strictly increasing: %v vs. %v",
					objA.metadata.Name,
					objB.metadata.Name))
		}
	}

	// Make sure prevGeneration is an upper bound for object generation numbers.
	for _, o := range b.objects {
		if !(o.metadata.Generation <= b.prevGeneration) {
			panic(
				fmt.Sprintf(
					"Object generation %v exceeds %v",
					o.metadata.Generation,
					b.prevGeneration))
		}
	}
}

func (b *bucket) Name() string {
	return b.name
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) ListObjects(
	ctx context.Context,
	query *storage.Query) (listing *storage.Objects, err error) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	// Set up the result object.
	listing = new(storage.Objects)

	// Handle nil queries.
	if query == nil {
		query = &storage.Query{}
	}

	// Handle defaults.
	maxResults := query.MaxResults
	if maxResults == 0 {
		maxResults = 1000
	}

	// Find where in the space of object names to start.
	nameStart := query.Prefix
	if query.Cursor != "" && query.Cursor > nameStart {
		nameStart = query.Cursor
	}

	// Find the range of indexes within the array to scan.
	indexStart := b.objects.lowerBound(nameStart)
	prefixLimit := b.objects.prefixUpperBound(query.Prefix)
	indexLimit := minInt(indexStart+maxResults, prefixLimit)

	// Scan the array.
	var lastResultWasPrefix bool
	for i := indexStart; i < indexLimit; i++ {
		var o object = b.objects[i]
		name := o.metadata.Name

		// Search for a delimiter if necessary.
		if query.Delimiter != "" {
			// Search only in the part after the prefix.
			nameMinusQueryPrefix := name[len(query.Prefix):]

			delimiterIndex := strings.Index(nameMinusQueryPrefix, query.Delimiter)
			if delimiterIndex >= 0 {
				resultPrefixLimit := delimiterIndex

				// Transform to an index within name.
				resultPrefixLimit += len(query.Prefix)

				// Include the delimiter in the result.
				resultPrefixLimit += len(query.Delimiter)

				// Save the result, but only if it's not a duplicate.
				resultPrefix := name[:resultPrefixLimit]
				if len(listing.Prefixes) == 0 ||
					listing.Prefixes[len(listing.Prefixes)-1] != resultPrefix {
					listing.Prefixes = append(listing.Prefixes, resultPrefix)
				}

				lastResultWasPrefix = true
				continue
			}
		}

		lastResultWasPrefix = false

		// Otherwise, save as an object result.
		listing.Results = append(listing.Results, o.metadata)
	}

	// Set up a cursor for where to start the next scan if we didn't exhaust the
	// results.
	if indexLimit < prefixLimit {
		listing.Next = &storage.Query{}
		*listing.Next = *query

		// Ion is if the final object we visited was returned as an element in
		// listing.Prefixes, we want to skip all other objects that would result in
		// the same so we don't return duplicate elements in listing.Prefixes
		// accross requests.
		if lastResultWasPrefix {
			lastResultPrefix := listing.Prefixes[len(listing.Prefixes)-1]
			listing.Next.Cursor = prefixSuccessor(lastResultPrefix)

			// Check an assumption: prefixSuccessor cannot result in the empty string
			// above because object names must be non-empty UTF-8 strings, and there
			// is no valid non-empty UTF-8 string that consists of entirely 0xff
			// bytes.
			if listing.Next.Cursor == "" {
				err = errors.New("Unexpected empty string from prefixSuccessor")
				return
			}
		} else {
			// Otherwise, we'll start scanning at the next object.
			listing.Next.Cursor = b.objects[indexLimit].metadata.Name
		}
	}

	return
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) NewReader(
	ctx context.Context,
	objectName string) (io.ReadCloser, error) {
	b.mu.RLock()
	defer b.mu.RUnlock()

	index := b.objects.find(objectName)
	if index == len(b.objects) {
		return nil, errors.New("object doesn't exist.")
	}

	return ioutil.NopCloser(strings.NewReader(b.objects[index].contents)), nil
}

func (b *bucket) NewWriter(
	ctx context.Context,
	attrs *storage.ObjectAttrs) (gcs.ObjectWriter, error) {
	// Check that the object name is legal.
	name := attrs.Name
	if len(name) == 0 || len(name) > 1024 {
		return nil, errors.New("Invalid object name: length must be in [1, 1024]")
	}

	if !utf8.ValidString(name) {
		return nil, errors.New("Invalid object name: not valid UTF-8")
	}

	for _, r := range name {
		if r == 0x0a || r == 0x0d {
			return nil, errors.New("Invalid object name: must not contain CR or LF")
		}
	}

	return newObjectWriter(b, attrs), nil
}

// LOCKS_EXCLUDED(b.mu)
func (b *bucket) DeleteObject(
	ctx context.Context,
	name string) error {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Do we possess the object with the given name?
	index := b.objects.find(name)
	if index == len(b.objects) {
		return errors.New("Object Not Found.")
	}

	// Remove the object.
	b.objects = append(b.objects[:index], b.objects[index+1:]...)

	return nil
}

// Create an object struct for the given attributes and contents.
//
// EXCLUSIVE_LOCKS_REQUIRED(b.mu)
func (b *bucket) mintObject(
	attrs *storage.ObjectAttrs,
	contents string) (o object) {
	// Set up basic metadata.
	b.prevGeneration++
	o.metadata = &storage.Object{
		Bucket:          b.Name(),
		Name:            attrs.Name,
		ContentType:     attrs.ContentType,
		ContentLanguage: attrs.ContentLanguage,
		CacheControl:    attrs.CacheControl,
		Owner:           "user-fake",
		Size:            int64(len(contents)),
		CRC32C:          crc32.Checksum([]byte(contents), crc32Table),
		MediaLink:       "http://localhost/download/storage/fake/" + attrs.Name,
		Metadata:        attrs.Metadata,
		Generation:      b.prevGeneration,
		MetaGeneration:  1,
		StorageClass:    "STANDARD",
	}

	// Match GCS's behavior for default ContentType.
	if o.metadata.ContentType == "" {
		o.metadata.ContentType = "application/octet-stream"
	}

	// Fill in the MD5 field.
	md5Array := md5.Sum([]byte(contents))
	o.metadata.MD5 = md5Array[:]

	// Set up contents.
	o.contents = contents

	return
}

// Add a record for an object with the given attributes and contents, then
// return the minted metadata.
//
// LOCKS_EXCLUDED(b.mu)
func (b *bucket) addObject(
	attrs *storage.ObjectAttrs,
	contents string) *storage.Object {
	b.mu.Lock()
	defer b.mu.Unlock()

	// Create an object record from the given attributes.
	var o object = b.mintObject(attrs, contents)

	// Replace an entry in or add an entry to our list of objects.
	existingIndex := b.objects.find(attrs.Name)
	if existingIndex < len(b.objects) {
		b.objects[existingIndex] = o
	} else {
		b.objects = append(b.objects, o)
		sort.Sort(b.objects)
	}

	return o.metadata
}

func minInt(a, b int) int {
	if a < b {
		return a
	}

	return b
}

func maxInt(a, b int) int {
	if a > b {
		return a
	}

	return b
}
<|endoftext|>


<commit_before><commit_msg>Use net.IPv4zero for 0.0.0.0.<commit_after><|endoftext|>