blob: 113153097371af4398bbf40a0eafe74653e2b049 [file] [log] [blame]
// Exercises basic store operations under various conditions.
//
// NOTE(sadovsky):
// - It would be nice if Dir were a builtin (provided by store).
// - It would be nice to have "mkdir -p" functionality for "put" commands.
// - It's not clear how to decide between map[string]store.ID and the implicit
// subdir mechanism.
// - It'd be nice to not have to specify names for list items in the case where
// we use the implicit subdir mechanism (rather than a slice).
// - Why does "vgo test" take so long to compile? I thought Go compilation was
// supposed to be fast...
package test
import (
"fmt"
"log"
"runtime"
"testing"
bb "veyron/lib/testutil/blackbox"
"veyron2/storage"
"veyron2/storage/vstore/primitives"
"veyron2/vom"
)
func init() {
bb.CommandTable["startServerRemote"] = startServerRemote
}
func vomRegister() {
vom.Register(&List{})
vom.Register(&Todo{})
}
func TestHelperProcess(t *testing.T) {
bb.HelperProcess(t)
}
func startServerRemote(argv []string) {
getRuntime() // initialize Runtime
if len(argv) > 1 || (len(argv) == 1 && argv[0] != "vomRegister") {
log.Fatal("Failed to start remote server: ", argv)
} else if len(argv) == 1 {
vomRegister()
}
name, cl := startServer()
fmt.Println("ready")
fmt.Println(name)
bb.WaitForEOFOnStdin()
fmt.Println("done")
cl()
}
////////////////////////////////////////////////////////////////////////////////
// Structs and helpers
// Dir is a "directory" in the store.
type Dir struct{}
// List is a list of Todo items.
type List struct {
Dir
// TODO(sadovsky): Should we hold a slice (or map) of Todo names here, or
// simply prefix Todo names with List names, e.g.
// /lists/[list_name]/[todo_name]? For now, we do the former, since the query
// and name server listdir APIs are not yet implemented.
Todos []storage.ID
}
// Todo is a single task to be done.
type Todo struct {
Dir
Text string
Done bool
Tags []string
}
func newDir() *Dir {
return &Dir{}
}
func newList() *List {
return &List{Dir: *newDir()}
}
func newTodo(text string) *Todo {
return &Todo{Dir: *newDir(), Text: text}
}
////////////////////////////////////////////////////////////////////////////////
// Type-specific helpers
func getList(t *testing.T, st storage.Store, tr storage.Transaction, path string) *List {
_, file, line, _ := runtime.Caller(1)
v := get(t, st, tr, path)
res, ok := v.(*List)
if !ok {
t.Fatalf("%s(%d): %s: not a List: %v", file, line, path, v)
}
return res
}
func getTodo(t *testing.T, st storage.Store, tr storage.Transaction, path string) *Todo {
_, file, line, _ := runtime.Caller(1)
v := get(t, st, tr, path)
res, ok := v.(*Todo)
if !ok {
t.Fatalf("%s(%d): %s: not a Todo: %v", file, line, path, v)
}
return res
}
////////////////////////////////////////////////////////////////////////////////
// Test cases
func testTodos(t *testing.T, st storage.Store) {
// Create lists.
{
// NOTE(sadovsky): Currently, we can't put /x/y until we put / and /x.
tr := primitives.NewTransaction()
put(t, st, tr, "/", newDir())
put(t, st, tr, "/lists", newDir())
put(t, st, tr, "/lists/drinks", newList())
put(t, st, tr, "/lists/snacks", newList())
commit(t, tr)
}
// Add some todos.
{
tr := primitives.NewTransaction()
// NOTE(sadovsky): It feels awkward to create my own names (ids) for these
// Todo objects. I'd like some way to create them in some "directory"
// without explicitly naming them. I.e. in this case I want to think of the
// directory as a list, not a map.
put(t, st, tr, "/lists/drinks/Todos/@", newTodo("milk"))
put(t, st, tr, "/lists/drinks/Todos/@", newTodo("beer"))
put(t, st, tr, "/lists/snacks/Todos/@", newTodo("chips"))
commit(t, tr)
}
// Verify some of the photos.
{
tr := primitives.NewTransaction()
todo := getTodo(t, st, tr, "/lists/drinks/Todos/0")
if todo.Text != "milk" {
t.Errorf("Expected %q, got %q", "milk", todo.Text)
}
}
{
tr := primitives.NewTransaction()
todo := getTodo(t, st, tr, "/lists/snacks/Todos/0")
if todo.Text != "chips" {
t.Errorf("Expected %q, got %q", "chips", todo.Text)
}
}
// Move a todo item from one list to another.
{
tr := primitives.NewTransaction()
todo := getTodo(t, st, tr, "/lists/drinks/Todos/1")
// NOTE(sadovsky): Remove works for map entries, but not yet for slices.
// Instead, we read the list, prune it, and write it back.
//remove(t, st, tr, "/lists/drinks/Todos/1")
list := getList(t, st, tr, "/lists/drinks")
list.Todos = list.Todos[:1]
put(t, st, tr, "lists/drinks", list)
put(t, st, tr, "/lists/snacks/Todos/@", todo)
commit(t, tr)
}
// Verify that the original todo is no longer there.
// TODO(sadovsky): Use queries to verify that both lists have changed.
{
tr := primitives.NewTransaction()
// Note, this will be much prettier in veyron2.
_, file, line, _ := runtime.Caller(1)
path := "/lists/drinks/1"
if _, err := st.Bind(path).Get(tr); err == nil {
t.Fatalf("%s(%d): got removed object %s", file, line, path)
}
}
}
func TestTodosWithLocalServer(t *testing.T) {
// Initialize Runtime and register vom types (for both client and server).
getRuntime()
vomRegister()
st, cl := startServerAndMakeClient()
defer cl()
testTodos(t, st)
}
func testTodosWithRemoteServer(t *testing.T, doVomRegister bool) {
// Initialize Runtime and register vom types (for client but not server).
getRuntime()
vomRegister()
args := []string{}
if doVomRegister {
args = append(args, "vomRegister")
}
server := bb.HelperCommand(t, "startServerRemote", args...)
defer server.Cleanup()
server.Cmd.Start()
server.Expect("ready") // wait for server to be ready
oa, err := server.ReadLineFromChild()
if err != nil {
t.Fatal("Failed to read server OA: %v", err)
}
st, cl := makeClient(oa)
defer cl()
testTodos(t, st)
server.CloseStdin()
server.Expect("done")
server.ExpectEOFAndWait()
}
func TestTodosWithRemoteServer(t *testing.T) {
testTodosWithRemoteServer(t, true)
}
// TODO(sadovsky): This test fails with the following error because the vom
// types aren't registered with the store server. We need to fix the store to
// support unregistered types.
// store_test.go(146): can't put /: ipc: response decoding failed: EOF
func DisabledTestTodosWithRemoteServerNoRegister(t *testing.T) {
testTodosWithRemoteServer(t, false)
}