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

// Without any build tags the tests are run on libkbfs directly.
// With the tag dokan all tests are run through a dokan filesystem.
// With the tag fuse all tests are run through a fuse filesystem.
// Note that fuse cannot be compiled on Windows and Dokan can only
// be compiled on Windows.

package test

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/kbfs/libfs"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

type createUserFn func(t testing.TB, ith int, config *libkbfs.ConfigLocal,
	opTimeout time.Duration) User

type fsEngine struct {
	name       string
	t          testing.TB
	createUser createUserFn
}
type fsNode struct {
	path string
}

type fsUser struct {
	mntDir string
	config *libkbfs.ConfigLocal
	cancel func()
	close  func()
}

// Perform Init for the engine
func (*fsEngine) Init() {}

// Name returns the name of the Engine.
func (e *fsEngine) Name() string {
	return e.name
}

// GetUID is called by the test harness to retrieve a user instance's UID.
func (e *fsEngine) GetUID(user User) keybase1.UID {
	u := user.(*fsUser)
	_, uid, err := u.config.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		e.t.Fatalf("GetUID: GetCurrentUserInfo failed with %v", err)
	}
	return uid
}

func buildRootPath(u *fsUser, isPublic bool) string {
	var path string
	if isPublic {
		// TODO: Consolidate all "public" and "private"
		// constants in libkbfs.
		path = filepath.Join(u.mntDir, "public")
	} else {
		path = filepath.Join(u.mntDir, "private")
	}
	return path
}

func buildTlfPath(u *fsUser, tlfName string, isPublic bool) string {
	return filepath.Join(buildRootPath(u, isPublic), tlfName)
}

func (e *fsEngine) GetFavorites(user User, public bool) (map[string]bool, error) {
	u := user.(*fsUser)
	path := buildRootPath(u, public)
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	fis, err := f.Readdir(-1)
	if err != nil {
		return nil, fmt.Errorf("Readdir on %v failed: %q", f, err.Error())
	}
	favorites := make(map[string]bool)
	for _, fi := range fis {
		favorites[fi.Name()] = true
	}
	return favorites, nil
}

// GetRootDir implements the Engine interface.
func (e *fsEngine) GetRootDir(user User, tlfName string, isPublic bool, expectedCanonicalTlfName string) (dir Node, err error) {
	u := user.(*fsUser)
	path := buildTlfPath(u, tlfName, isPublic)
	var realPath string
	// TODO currently we pretend that Dokan has no symbolic links
	// here and end up deferencing them. This works but is not
	// ideal. (See Lookup.)
	if tlfName == expectedCanonicalTlfName || e.name == "dokan" {
		realPath = path
	} else {
		realPath, err = filepath.EvalSymlinks(path)
		if err != nil {
			return nil, err
		}
		canonicalTlfName := filepath.Base(realPath)
		if canonicalTlfName != expectedCanonicalTlfName {
			return nil, fmt.Errorf(
				"Expected canonical TLF name %s, got %s",
				expectedCanonicalTlfName, canonicalTlfName)
		}
	}
	return fsNode{realPath}, nil
}

// CreateDir is called by the test harness to create a directory relative to the passed
// parent directory for the given user.
func (*fsEngine) CreateDir(u User, parentDir Node, name string) (dir Node, err error) {
	p := parentDir.(fsNode)
	path := filepath.Join(p.path, name)
	err = os.Mkdir(path, 0755)
	if err != nil {
		return nil, err
	}
	return fsNode{path}, nil
}

// CreateFile is called by the test harness to create a file in the given directory as
// the given user.
func (*fsEngine) CreateFile(u User, parentDir Node, name string) (file Node, err error) {
	p := parentDir.(fsNode)
	path := filepath.Join(p.path, name)
	f, err := os.Create(path)
	if err != nil {
		return nil, err
	}
	f.Close()
	return fsNode{path}, nil
}

// CreateFileExcl is called by the test harness to exclusively create a file in
// the given directory as the given user. The file is created with
// O_RDWR|O_CREATE|O_EXCL.
func (*fsEngine) CreateFileExcl(u User, parentDir Node, name string) (file Node, err error) {
	p := parentDir.(fsNode).path
	f, err := os.OpenFile(filepath.Join(p, name), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		return nil, err
	}
	f.Close()
	return fsNode{p}, nil
}

// WriteFile is called by the test harness to write to the given file as the given user.
func (*fsEngine) WriteFile(u User, file Node, data []byte, off int64, sync bool) (err error) {
	n := file.(fsNode)
	f, err := os.OpenFile(n.path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.Write(data)
	if err != nil {
		return err
	}
	if !sync {
		return nil
	}
	return f.Sync()
}

// TruncateFile is called by the test harness to truncate the given file as the given user to the given size.
func (*fsEngine) TruncateFile(u User, file Node, size uint64, sync bool) (err error) {
	n := file.(fsNode)
	f, err := os.OpenFile(n.path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	err = f.Truncate(int64(size))
	if err != nil {
		return err
	}<commit_msg>engine_fs_test: seek on a write when the offset is non-zero<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

// Without any build tags the tests are run on libkbfs directly.
// With the tag dokan all tests are run through a dokan filesystem.
// With the tag fuse all tests are run through a fuse filesystem.
// Note that fuse cannot be compiled on Windows and Dokan can only
// be compiled on Windows.

package test

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol"
	"github.com/keybase/kbfs/libfs"
	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

type createUserFn func(t testing.TB, ith int, config *libkbfs.ConfigLocal,
	opTimeout time.Duration) User

type fsEngine struct {
	name       string
	t          testing.TB
	createUser createUserFn
}
type fsNode struct {
	path string
}

type fsUser struct {
	mntDir string
	config *libkbfs.ConfigLocal
	cancel func()
	close  func()
}

// Perform Init for the engine
func (*fsEngine) Init() {}

// Name returns the name of the Engine.
func (e *fsEngine) Name() string {
	return e.name
}

// GetUID is called by the test harness to retrieve a user instance's UID.
func (e *fsEngine) GetUID(user User) keybase1.UID {
	u := user.(*fsUser)
	_, uid, err := u.config.KBPKI().GetCurrentUserInfo(context.Background())
	if err != nil {
		e.t.Fatalf("GetUID: GetCurrentUserInfo failed with %v", err)
	}
	return uid
}

func buildRootPath(u *fsUser, isPublic bool) string {
	var path string
	if isPublic {
		// TODO: Consolidate all "public" and "private"
		// constants in libkbfs.
		path = filepath.Join(u.mntDir, "public")
	} else {
		path = filepath.Join(u.mntDir, "private")
	}
	return path
}

func buildTlfPath(u *fsUser, tlfName string, isPublic bool) string {
	return filepath.Join(buildRootPath(u, isPublic), tlfName)
}

func (e *fsEngine) GetFavorites(user User, public bool) (map[string]bool, error) {
	u := user.(*fsUser)
	path := buildRootPath(u, public)
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	fis, err := f.Readdir(-1)
	if err != nil {
		return nil, fmt.Errorf("Readdir on %v failed: %q", f, err.Error())
	}
	favorites := make(map[string]bool)
	for _, fi := range fis {
		favorites[fi.Name()] = true
	}
	return favorites, nil
}

// GetRootDir implements the Engine interface.
func (e *fsEngine) GetRootDir(user User, tlfName string, isPublic bool, expectedCanonicalTlfName string) (dir Node, err error) {
	u := user.(*fsUser)
	path := buildTlfPath(u, tlfName, isPublic)
	var realPath string
	// TODO currently we pretend that Dokan has no symbolic links
	// here and end up deferencing them. This works but is not
	// ideal. (See Lookup.)
	if tlfName == expectedCanonicalTlfName || e.name == "dokan" {
		realPath = path
	} else {
		realPath, err = filepath.EvalSymlinks(path)
		if err != nil {
			return nil, err
		}
		canonicalTlfName := filepath.Base(realPath)
		if canonicalTlfName != expectedCanonicalTlfName {
			return nil, fmt.Errorf(
				"Expected canonical TLF name %s, got %s",
				expectedCanonicalTlfName, canonicalTlfName)
		}
	}
	return fsNode{realPath}, nil
}

// CreateDir is called by the test harness to create a directory relative to the passed
// parent directory for the given user.
func (*fsEngine) CreateDir(u User, parentDir Node, name string) (dir Node, err error) {
	p := parentDir.(fsNode)
	path := filepath.Join(p.path, name)
	err = os.Mkdir(path, 0755)
	if err != nil {
		return nil, err
	}
	return fsNode{path}, nil
}

// CreateFile is called by the test harness to create a file in the given directory as
// the given user.
func (*fsEngine) CreateFile(u User, parentDir Node, name string) (file Node, err error) {
	p := parentDir.(fsNode)
	path := filepath.Join(p.path, name)
	f, err := os.Create(path)
	if err != nil {
		return nil, err
	}
	f.Close()
	return fsNode{path}, nil
}

// CreateFileExcl is called by the test harness to exclusively create a file in
// the given directory as the given user. The file is created with
// O_RDWR|O_CREATE|O_EXCL.
func (*fsEngine) CreateFileExcl(u User, parentDir Node, name string) (file Node, err error) {
	p := parentDir.(fsNode).path
	f, err := os.OpenFile(filepath.Join(p, name), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
	if err != nil {
		return nil, err
	}
	f.Close()
	return fsNode{p}, nil
}

// WriteFile is called by the test harness to write to the given file as the given user.
func (*fsEngine) WriteFile(u User, file Node, data []byte, off int64, sync bool) (err error) {
	n := file.(fsNode)
	f, err := os.OpenFile(n.path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	_, err = f.Seek(off, 0)
	if err != nil {
		return err
	}
	_, err = f.Write(data)
	if err != nil {
		return err
	}
	if !sync {
		return nil
	}
	return f.Sync()
}

// TruncateFile is called by the test harness to truncate the given file as the given user to the given size.
func (*fsEngine) TruncateFile(u User, file Node, size uint64, sync bool) (err error) {
	n := file.(fsNode)
	f, err := os.OpenFile(n.path, os.O_RDWR|os.O_CREATE, 0644)
	if err != nil {
		return err
	}
	defer f.Close()
	err = f.Truncate(int64(size))
	if err != nil {
		return err
	}
{'fraction_non_alphanumeric': 0.11924882629107982, 'fraction_numerical': 0.005539906103286385, 'mean_word_length': 3.683817062445031, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3357873', 'n_tokens_mistral': 3943, 'n_tokens_neox': 3767, 'n_words': 1289}