Repository: C2FO/vfs
Pull request #26 (2019-07-12 19:41:48)

In memory backend
This PR contains an updated version of the in-memory backend (thread safety added) as well as updates to the backend integration test to include the in-memory backend in the tests.  


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b439525..f2ab1fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+## [5.2.0] - 2019-07-16
+### Added
+- In-Memory backend vfs implementation
+- Included the in-memory backend to the list of registered backends used in backend_integration_test
+- Checking for existence at the start of various File functions (size, seek, etc) and returning and error if the file does not exist on the OS backend
+- Tests in  backend_integration_test that ensure that operations done on non-existent files throw an error
+- mem.md in vfs/docs, updated link to it in the README.md
+### Fixed
+- Relative path validation in utils.go now disallows empty names
+### Changed
+- utils_test.go now expects an empty string NOT to validate
+- updated README to include "Touch()" definition under the File interface rather than the Location interface
+- updated README to exclude "in-memory-backend" from the list of ideas
+
+
 ## [5.1.0] - 2019-07-08
 ### Added
 - Added Touch() method to File interface and implemented in each backend.
diff --git a/README.md b/README.md
index c084cee..58b1e11 100644
--- a/README.md
+++ b/README.md
@@ -134,6 +134,7 @@ Feel free to send a pull request if you want to add your backend to the list.
   * [os backend](docs/os.md)
   * [gs backend](docs/gs.md)
   * [s3 backend](docs/s3.md)
+  * [in-memory backend](docs/mem.md)
 * [utils](docs/utils.md)
 
 ### Ideas
@@ -142,7 +143,6 @@ Things to add:
 
 * Add SFTP backend
 * Add Azure storage backend
-* Add in-memory backend
 * Provide better List() functionality with more abstracted filering and paging (iterator?) Retrun File structs vs URIs?
 * Add better/any context.Context() support
 
@@ -266,6 +266,10 @@ type File interface {
 	//
 	// For file:///some/path/to/file.txt, it would return file.txt
 	Name() string
+	
+	// Touch creates a zero-length file on the vfs.File if no File exists.  Update File's last modified timestamp.
+    	// Returns error if unable to touch File.
+        Touch() error
 
 	// URI returns the fully qualified absolute URI for the File.  IE, s3://bucket/some/path/to/file.txt
 	URI() string
@@ -302,7 +306,7 @@ type FileSystem interface {
 	NewLocation(volume string, absLocPath string) (Location, error)
 
 	// Name returns the name of the FileSystem ie: Amazon S3, os, Google Cloud Storage, etc.
-	Name() string
+	Name() string	
 
 	// Scheme returns the uri scheme used by the FileSystem: s3, file, gs, etc.
 	Scheme() string
@@ -403,10 +407,6 @@ type Location interface {
 	// * Accepts relative file path.
 	DeleteFile(relFilePath string) error
 
-	// Touch creates a zero-length file on the vfs.File if no File exists.  Update File's last modified timestamp.
-	// Returns error if unable to touch File.
-	Touch() error
-
 	// URI returns the fully qualified absolute URI for the Location.  IE, s3://bucket/some/path/
 	//
 	// URI's for locations must always end with a slash.
diff --git a/backend/mem/doc.go b/backend/mem/doc.go
new file mode 100644
index 0000000..4934dc8
--- /dev/null
+++ b/backend/mem/doc.go
@@ -0,0 +1,20 @@
+/*
+Package mem built-in mem lib VFS implementation.
+Usage
+Rely on github.com/c2fo/vfs/backend
+  import(
+      "github.com/c2fo/vfs/backend"
+      "github.com/c2fo/vfs/backend/mem"
+  )
+  func UseFs() error {
+      fs, err := backend.Backend(mem.Scheme)
+      ...
+  }
+Or call directly:
+  import _mem "github.com/c2fo/vfs/backend/mem"
+  func DoSomething() {
+	fs := _mem.NewFileSystem()
+      ...
+  }
+*/
+package mem
diff --git a/backend/mem/file.go b/backend/mem/file.go
new file mode 100644
index 0000000..4214e13
--- /dev/null
+++ b/backend/mem/file.go
@@ -0,0 +1,598 @@
+package mem
+
+import (
+	"bytes"
+	"errors"
+	"io"
+	"path"
+	"sync"
+	"time"
+
+	"github.com/c2fo/vfs/v5"
+	"github.com/c2fo/vfs/v5/utils"
+)
+
+//
+//	memFile with name "filename" is the single representation of the structure that
+//	all calls to "filename" will reference. The point of doing it this way is
+//	to allow multiple read threads, and locking up writing and deletion.
+//	Files referencing filename will check their own 'contents' slice against
+//	"filename's" and is updated accordingly. This allows multiple threads to have the most
+//	up-to-date file contents while also preserving the state of their own cursor that is
+//	used in Read() and Seek() calls. Calls to Write() all reference
+// 	"filename's" buffer, which is why it is locked. Data written into the writeBuffer
+//	does not appear in "filename's" 'contents' slice until a call to Close() is made
+//
+type memFile struct {
+	sync.Mutex
+	writeBuffer  *bytes.Buffer
+	exists       bool
+	contents     []byte
+	location     vfs.Location
+	lastModified time.Time
+	name         string
+	isOpen       bool
+	filepath     string
+}
+
+
+//File implements vfs.File interface for the in-memory implementation of FileSystem.
+//A file struct holds a pointer to a single memFile.  Multiple threads will refer to the same
+//memFile. Simultaneous reading is allowed, but writing and closing are protected by locks.
+type File struct {
+	memFile  *memFile
+	exists   bool
+	isOpen   bool
+	contents []byte //the file contents
+	name     string //the base name of the file
+	cursor   int    //the index that the buffer (contents) is at
+
+}
+
+//		////// Error Functions ///////		//
+func doesNotExist() error {
+	return errors.New("This file does not exist!")
+}
+
+func nilReference() error {
+	return errors.New("The target file passed in was nil")
+}
+
+func seekError() error {
+	return errors.New("Seek could not complete the desired call")
+}
+
+///////////////////////////////////////////////////////////
+
+//Close imitates io.Closer by resetting the cursor and setting a boolean
+func (f *File) Close() error {
+
+	if f == nil {
+		return nilReference()
+	}
+	f.memFile.Lock()
+	if f.memFile.writeBuffer.Len() > 0 {
+		bufferContents := f.memFile.writeBuffer.Bytes()
+		for i, _ := range bufferContents {
+			f.memFile.contents = append(f.memFile.contents, bufferContents[i])
+
+		}
+		f.memFile.lastModified = time.Now()
+		f.memFile.writeBuffer = new(bytes.Buffer)
+	}
+
+	f.memFile.isOpen = false
+	f.cursor = 0
+	f.memFile.Unlock()
+	return nil
+}
+
+//Read implements the io.Reader interface.  Returns number of bytes read and potential errors
+func (f *File) Read(p []byte) (n int, err error) {
+
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return 0, err
+		}
+		return 0, doesNotExist()
+	}
+	//if file exists:
+	offset := f.cursor
+	if f.isOpen == false {
+		f.isOpen = true
+	}
+
+	readBufferLength := len(p)
+	//readBufferLength of byte slice is zero, just return 0 and nil
+	if readBufferLength == 0 {
+		return 0, nil
+	}
+	//in case the file contents have changed
+	f.synchronize()
+
+	fileContentLength := len(f.contents)
+	//if the cursor is at the end of the file
+	if f.cursor == fileContentLength {
+		return 0, io.EOF
+	}
+	//j is the incrementer for the readBuffer. It always starts at 0, but the cursor may not
+	j := 0
+	for i := range p {
+		if !f.isOpen {
+			return i, errors.New("file is closed")
+		}
+		//if "i" is greater than the readBufferLength of p or readBufferLength of the contents
+		if i == readBufferLength || f.cursor == fileContentLength {
+			return i, io.EOF
+		}
+		//otherwise simply copy each index to p
+		p[i] = f.contents[f.cursor]
+		j++
+		f.cursor++
+
+	}
+	if f.cursor > len(f.contents) {
+		f.cursor = len(f.contents)
+	}
+	return readBufferLength - offset, nil
+
+}
+
+//Seek implements the io.Seeker interface.  Returns the current position of the cursor and errors if any
+func (f *File) Seek(offset int64, whence int) (int64, error) {
+
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return 0, err
+		}
+		return 0, doesNotExist()
+	}
+
+	length := len(f.contents)
+	if length == 0 && offset == 0 && whence == 0 {
+		return 0, nil
+	}
+
+	switch whence {
+
+	case 0:
+		if int(offset) < length && offset >= 0 {
+			f.cursor = int(offset)
+			return offset, nil
+		} else {
+			return int64(f.cursor), seekError()
+		}
+
+	case 1:
+		pos := f.cursor + int(offset)
+		if pos < length && pos >= 0 {
+			f.cursor = pos
+			return int64(pos), nil
+		} else {
+			return int64(f.cursor), seekError()
+		}
+
+	case 2:
+		pos := length + int(offset)
+		if pos < length && pos >= 0 {
+			f.cursor = pos
+			return int64(f.cursor), nil
+		} else {
+			return int64(f.cursor), seekError()
+		}
+
+	default:
+		return 0, seekError()
+
+	}
+
+}
+
+//Write implements the io.Writer interface. Returns number of bytes written and any errors
+func (f *File) Write(p []byte) (int, error) {
+	if !f.isOpen {
+		f.isOpen = true
+	}
+
+	if ex, err := f.Exists(); !ex {
+		if err != nil {
+			return 0, err
+		}
+		err = f.Touch()
+		if err != nil {
+			return 0, err
+		}
+	}
+	f.memFile.Lock()
+	num, err := f.memFile.writeBuffer.Write(p)
+	f.memFile.lastModified = time.Now()
+	f.memFile.Unlock()
+	return num, err
+
+}
+
+//String implements the io.Stringer interface. It returns a string representation of the file's URI
+func (f *File) String() string {
+	return f.URI()
+}
+
+//Exists returns whether or not a file exists.  Creating a file does not
+//guarantee its existence, but creating one and writing to it does
+func (f *File) Exists() (bool, error) {
+
+	if f != nil {
+		//does it exist on the map?
+		vol := f.Location().Volume()
+		fullPath := f.Path()
+		loc := f.Location().(*Location)
+		mapRef := loc.fileSystem.fsMap
+		if _, ok := mapRef[vol]; ok {
+			if object, ok2 := mapRef[vol][fullPath]; ok2 {
+
+				if object != nil && object.i.(*memFile).exists {
+					return true, nil
+				}
+			}
+		}
+		return false, nil
+	}
+
+	return false, nilReference()
+}
+
+//Location simply returns the file's underlying location struct pointer
+func (f *File) Location() vfs.Location {
+
+	newLoc, err := f.memFile.location.FileSystem().NewLocation(f.memFile.location.Volume(), f.memFile.location.Path())
+	if err != nil {
+		panic(err)
+	}
+	return newLoc
+}
+
+//CopyToLocation copies the current file to the given location.  If file exists
+//at given location contents are simply overwritten using "CopyToFile", otherwise
+//a newFile is made, takes the contents of the current file, and ends up at
+//the given location
+func (f *File) CopyToLocation(location vfs.Location) (vfs.File, error) {
+
+	if ok, err := f.Exists(); !ok {
+		if err != nil {
+			return nil, err
+		}
+		return nil, doesNotExist()
+	}
+	testPath := path.Join(path.Clean(location.Path()), f.Name())
+	thisLoc := f.Location().(*Location)
+	mapRef := thisLoc.fileSystem.fsMap
+	vol := thisLoc.Volume()
+	//making sure that this volume has keys at all
+	if _, ok := mapRef[vol]; ok {
+		//if file w/name exists @ loc, simply copy contents over
+		if _, ok2 := mapRef[vol][testPath]; ok2 {
+			//casting fsObject to a file
+			memFile := mapRef[vol][testPath].i.(*memFile)
+			file := deepCopy(memFile)
+
+			cerr := f.CopyToFile(file)
+
+			if cerr != nil {
+				return nil, cerr
+			}
+			return file, nil
+		}
+	} //end outer-if
+
+	newFile, err := location.NewFile(f.Name())
+	if err != nil {
+		return nil, err
+	}
+	//writing to existence. Whatever the underlying vfs implementation is will take care of this
+	if _, err = newFile.Write(make([]byte, 0)); err != nil {
+		return nil, err
+	}
+
+	if err = f.CopyToFile(newFile); err != nil {
+		return nil, err
+	}
+
+	return newFile, nil
+}
+
+//CopyToFile copies the receiver file into the target file.
+//The target file is deleted, so any references to it will
+//be nil.  In order to access the target after calling CopyToFile
+//use its previous path to call it using the fsMap.  Additionally,
+//after this is called, f's cursor will reset as if it had been closed.
+func (f *File) CopyToFile(target vfs.File) error {
+
+	if f == nil || target == nil {
+		return nilReference()
+	}
+
+	if ex, _ := f.Exists(); !ex {
+		return doesNotExist()
+	}
+
+	if target.Location().FileSystem().Scheme() == "mem" {
+		target.(*File).memFile.contents = make([]byte, 0)
+	}
+
+	if ex, err := target.Exists(); !ex {
+		if err == nil {
+
+			if _, err := target.Write(f.memFile.contents); err != nil {
+				return err
+			}
+			err = f.Close()
+			if err != nil {
+				return err
+			}
+			err = target.Close()
+			if err != nil {
+				return err
+			}
+		}
+
+	}
+	if _, err := target.Write(f.memFile.contents); err != nil {
+		return err
+	}
+	err := target.Close()
+	if err != nil {
+		return err
+	}
+	return f.Close()
+}
+
+//MoveToLocation moves the receiver file to the passed in location. It does so by
+//creating a copy of 'f' in "location".  'f' is subsequently  deleted
+func (f *File) MoveToLocation(location vfs.Location) (vfs.File, error) {
+
+	if f == nil || location == nil {
+		return nil, nilReference()
+	}
+
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return nil, err
+		}
+		return nil, doesNotExist()
+	}
+
+	// if the underling FileSystem is in-memory, then this is the native way of
+	//replacing a file with the same name as "f" at the location
+	if location.FileSystem().Scheme() == "mem" {
+		//this is a potential path to a file that can be fed into the objMap portion of fsMap
+		testPath := path.Join(location.Path(), f.Name())
+		loc := location.(*Location)
+		//mapRef just makes it easier to refer to "loc.fileSystem.fsMap"
+		mapRef := loc.fileSystem.fsMap
+		vol := loc.Volume()
+		f.memFile.location.FileSystem().(*FileSystem).Lock()
+		//this checks if the specified volume has any keys
+		if _, ok := mapRef[vol]; ok {
+			//this block checks if the file already exists at location, if it does, deletes it and inserts the file we have
+			if _, ok2 := mapRef[vol][testPath]; ok2 {
+				memFile := mapRef[vol][testPath].i.(*memFile)
+				f.memFile.location.FileSystem().(*FileSystem).Unlock()
+				file := deepCopy(memFile)
+				err := f.CopyToFile(file)
+				if err != nil {
+					return nil, err
+				}
+
+				err = f.Delete()
+				if err != nil {
+					return nil, err
+				}
+
+				return file, nil
+			}
+		}
+	}
+	f.memFile.location.FileSystem().(*FileSystem).Unlock()
+	// if the file doesn't yet exist at the location, create it there
+	newFile, err := location.NewFile(f.Name())
+	if err != nil {
+		return nil, err
+	}
+	//initialize the file
+	_, err = newFile.Write(make([]byte, 0))
+	if err != nil {
+		return nil, err
+	}
+	//copying over the data
+	err = f.CopyToFile(newFile)
+	if err != nil {
+		return nil, err
+	}
+	//delete the receiver
+	err = f.Delete()
+	if err != nil {
+		return nil, err
+	}
+	return newFile, nil
+}
+
+//MoveToFile creates a newFile, and moves it to "file".
+//The receiver is always deleted (since it's being "moved")
+func (f *File) MoveToFile(file vfs.File) error {
+	if f == nil {
+		return nilReference()
+	}
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return err
+		}
+		return doesNotExist()
+	}
+	if err := f.CopyToFile(file); err != nil {
+		return err
+	}
+
+	return f.Delete()
+}
+
+//Delete removes the file from the FileSystem. Sets it path in the fsMap to nil,
+//and also nils the file's members
+func (f *File) Delete() error {
+
+	if f == nil {
+		return nilReference()
+	}
+
+	if ex, _ := f.Exists(); !ex {
+		return doesNotExist()
+	}
+	f.memFile.Lock()
+	defer f.memFile.Unlock()
+	loc := f.Location().(*Location)
+	mapRef := loc.fileSystem.fsMap
+	f.memFile.location.FileSystem().(*FileSystem).Lock()
+	defer f.memFile.location.FileSystem().(*FileSystem).Unlock()
+	//if there are keys at this volume
+	if _, ok := mapRef[loc.Volume()]; ok {
+		//checking for the object that should contain the file at this key
+		if thisObj, ok2 := mapRef[loc.Volume()][f.Path()]; ok2 {
+			str := f.Path()
+			//casting a file to the object's "i" interface
+			file := thisObj.i.(*memFile)
+			file.exists = false
+			file = nil
+			thisObj.i = nil
+			thisObj = nil
+			//setting that key to nil so it truly no longer lives on this system
+			mapRef[loc.Volume()][str] = nil
+		}
+	}
+
+	return nil
+}
+
+func newMemFile(file *File, location vfs.Location) *memFile {
+
+	return &memFile{
+		sync.Mutex{},
+		new(bytes.Buffer),
+		false,
+		make([]byte, 0),
+		location,
+		time.Time{},
+		file.name,
+		false,
+		path.Join(location.Path(), file.Name()),
+	}
+}
+
+//LastModified simply returns the file's lastModified, if the file exists
+func (f *File) LastModified() (*time.Time, error) {
+	if f == nil {
+		return nil, nilReference()
+	}
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return nil, err
+		}
+		return nil, doesNotExist()
+	}
+	return &f.memFile.lastModified, nil
+}
+
+//Size returns the size of the file contents.  In our case, the length of the file's byte slice
+func (f *File) Size() (uint64, error) {
+	if f == nil {
+		return 0, nilReference()
+	}
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return 0, err
+		}
+		return 0, doesNotExist()
+	}
+	f.synchronize()
+	return uint64(len(f.contents)), nil
+
+}
+
+//Touch takes a in-memory vfs.File, makes it existent, and updates the lastModified
+func (f *File) Touch() error {
+	if f == nil {
+		return nilReference()
+	}
+	if f.memFile.exists {
+		f.exists = true
+		f.memFile.lastModified = time.Now()
+
+		return nil
+	}
+	f.memFile.exists = true
+	f.exists = true
+
+	volume := f.Location().Volume()
+	f.memFile.lastModified = time.Now()
+	//files and locations are contained in objects of type "fsObject".
+	// An fsObject has a blank interface and a boolean that indicates whether or not it is a file
+	fileObject := &fsObject{
+		true,
+		f.memFile,
+	}
+	locObject := &fsObject{
+		false,
+		f.Location(),
+	}
+
+	f.memFile.location.FileSystem().(*FileSystem).Lock()
+	defer f.memFile.location.FileSystem().(*FileSystem).Unlock()
+	//just a less clunky way of accessing the fsMap
+	mapRef := f.memFile.location.(*Location).fileSystem.fsMap
+	//if the objMap map does not exist for the volume yet, then we go ahead and create it.
+	if _, ok := mapRef[volume]; !ok {
+		mapRef[volume] = make(objMap)
+	}
+
+	//setting the map at Volume volume and path of f to this fileObject
+	mapRef[volume][f.Path()] = fileObject
+	f.memFile = mapRef[volume][f.Path()].i.(*memFile)
+	locationPath := utils.EnsureTrailingSlash(path.Clean(path.Dir(f.Path())))
+	//checking for locations that exist to avoid redundancy
+	if _, ok := mapRef[volume][locationPath]; !ok {
+		mapRef[volume][locationPath] = locObject
+	}
+	return nil
+}
+
+//Path returns the absolute path to the file
+func (f *File) Path() string {
+	if f == nil {
+		panic(nilReference())
+	}
+	return path.Join(f.Location().Path(), f.name)
+}
+
+//Name returns the basename of the file
+func (f *File) Name() string {
+	if f == nil {
+		panic(nilReference())
+	}
+	return f.name
+}
+
+//URI returns the file's URI, if it exists
+func (f *File) URI() string {
+	if f == nil {
+		panic(nilReference())
+	}
+	return utils.GetFileURI(f)
+}
+
+//synchronize updates a memFile's contents slice and cursor members
+func (f *File) synchronize() {
+	if f == nil {
+		panic(nilReference())
+	}
+	if ok := string(f.contents) != string(f.memFile.contents); ok {
+		f.cursor = 0
+		f.contents = f.memFile.contents
+	}
+}
diff --git a/backend/mem/fileSystem.go b/backend/mem/fileSystem.go
new file mode 100644
index 0000000..eba7c2b
--- /dev/null
+++ b/backend/mem/fileSystem.go
@@ -0,0 +1,174 @@
+package mem
+
+import (
+	"path"
+	"sync"
+
+	"github.com/c2fo/vfs/v5"
+	"github.com/c2fo/vfs/v5/backend"
+	"github.com/c2fo/vfs/v5/utils"
+)
+
+//Scheme defines the FileSystem type's underlying implementation.
+const Scheme = "mem"
+const name = "In-Memory Filesystem"
+
+type fsObject struct {
+	isFile bool
+	i      interface{}
+}
+type objMap map[string]*fsObject
+
+// FileSystem implements vfs.FileSystem for an in-memory file system.
+type FileSystem struct {
+	sync.Mutex
+	fsMap map[string]objMap
+}
+
+// Retry will return a retrier provided via options, or a no-op if none is provided.
+func (fs *FileSystem) Retry() vfs.Retry {
+	return vfs.DefaultRetryer()
+}
+
+//NewFile function returns the in-memory implementation of vfs.File.
+//Since this is inside FileSystem, we assume that the caller knows that the CWD is the root.
+//If a non-absolute path is given, an error is thrown. Additionally, a file does not
+//technically exist until a call to "Touch()" is made on it. The "Touch" call links the
+//file with FileSystem's map and brings it into existence.
+//If a file is written to before a touch call, Write() will take care of that call.  This is
+//true for other functions as well and existence only poses a problem in the context of deletion
+//or copying FROM a non-existent file.
+func (fs *FileSystem) NewFile(volume string, absFilePath string) (vfs.File, error) {
+
+	err := utils.ValidateAbsoluteFilePath(absFilePath)
+	if err != nil {
+		return nil, err
+	}
+
+	location, err := fs.NewLocation(volume, utils.EnsureTrailingSlash(path.Dir(absFilePath)))
+	if err != nil {
+		return nil, err
+	}
+	mapRef := location.(*Location).fileSystem.fsMap
+	if _, ok := mapRef[volume]; ok {
+		fileList := mapRef[volume].filesHere(location.Path())
+		for _, file := range fileList {
+			if file.name == path.Base(absFilePath) {
+				fileCopy := deepCopy(file)
+				file.location = location
+				fileCopy.(*File).memFile = file
+				return fileCopy, nil
+			}
+		}
+	}
+	//validateAbsFile path will throw an error if there was a trailing slash, hence not calling path.Clean()
+	file := &File{
+		name: path.Base(absFilePath),
+	}
+
+	memFile := newMemFile(file, location)
+	file.memFile = memFile
+	return file, nil
+}
+
+//NewLocation function returns the in-memory implementation of vfs.Location.
+//A location always exists. If a file is created on a location that has not yet
+//been made in the fsMap, then the location will be created with the file
+func (fs *FileSystem) NewLocation(volume string, absLocPath string) (vfs.Location, error) {
+
+	err := utils.ValidateAbsoluteLocationPath(absLocPath)
+	if err != nil {
+		return nil, err
+	}
+	str := utils.EnsureTrailingSlash(path.Clean(absLocPath))
+	return &Location{
+		fileSystem: fs,
+		name:       str,
+		exists:     false,
+		volume:     volume,
+	}, nil
+
+}
+
+// Name returns the name of the underlying FileSystem
+func (fs *FileSystem) Name() string {
+	return name
+}
+
+// Scheme returns the scheme of the underlying FileSystem
+func (fs *FileSystem) Scheme() string {
+	return Scheme
+}
+
+//NewFileSystem is used to initialize the file system struct for an in-memory FileSystem.
+func  NewFileSystem() *FileSystem {
+
+	return &FileSystem{
+		sync.Mutex{},
+		 make(map[string]objMap),
+	}
+
+}
+
+func init() {
+	//Even though the map is being made here, a call to
+	backend.Register(Scheme, NewFileSystem())
+
+}
+
+//getKeys is used to get a list of absolute paths on a specified volume. These paths are a mixture of files and locations
+func (o objMap) getKeys() []string {
+	keyList := make([]string, 0)
+	for i := range o {
+		keyList = append(keyList, i)
+	}
+	return keyList
+}
+
+//fileHere returns a list of file pointers found at the absolute location path provided.  If none are there, returns an empty slice
+func (o objMap) filesHere(absLocPath string) []*memFile {
+
+	paths := o.getKeys()
+	fileList := make([]*memFile, 0)
+	for i := range paths {
+
+		object := o[paths[i]]                         //retrieve the object
+		if ok := object != nil && object.isFile; ok { //if the object is a file, cast its interface, i, to a file and append to the slice
+			file := object.i.(*memFile)
+			if file.location.Path() == absLocPath {
+				fileList = append(fileList, file)
+			}
+		}
+	}
+	return fileList
+}
+
+//fileNamesHere returns a list of base names of files found at the absolute location path provided.  If none are there, returns an empty slice
+func (o objMap) fileNamesHere(absLocPath string) []string {
+
+	paths := o.getKeys()
+	fileList := make([]string, 0)
+	for i := range paths {
+
+		object := o[paths[i]]               //retrieve the object
+		if object != nil && object.isFile { //if the object is a file, cast its interface, i, to a file and append the name to the slice
+			file := object.i.(*memFile)
+			if utils.EnsureTrailingSlash(file.location.Path()) == absLocPath {
+				fileList = append(fileList, file.name)
+			}
+		}
+	}
+	return fileList
+}
+
+func deepCopy(srcFile *memFile) vfs.File {
+	destination := &File{
+		name: srcFile.name,
+	}
+
+	destination.memFile = srcFile
+	destination.exists = srcFile.exists
+	destination.isOpen = srcFile.isOpen
+	destination.contents = srcFile.contents
+	return destination
+}
diff --git a/backend/mem/location.go b/backend/mem/location.go
new file mode 100644
index 0000000..1fd042f
--- /dev/null
+++ b/backend/mem/location.go
@@ -0,0 +1,228 @@
+package mem
+
+import (
+	"errors"
+	"path"
+	"regexp"
+	"sort"
+	"strings"
+
+	"github.com/c2fo/vfs/v5"
+	"github.com/c2fo/vfs/v5/utils"
+)
+
+//Location implements the vfs.Location interface specific to in-memory FileSystem.
+type Location struct {
+	exists     bool
+	firstTime  bool
+	name       string //the path that this location exists on
+	fileSystem *FileSystem
+	volume     string
+}
+
+//String implements io.Stringer by returning the location's URI as a string
+func (l *Location) String() string {
+
+	return l.URI()
+}
+
+//List finds all of the files living at the current location and returns them in a slice of strings.
+//If there are no files at location, then an empty slice will be returned
+func (l *Location) List() ([]string, error) {
+
+	locPath := l.Path()
+	//setting mapRef to this value for code readability
+	mapRef := l.fileSystem.fsMap
+	//are there paths on this volume?
+	if _, ok := mapRef[l.Volume()]; ok {
+		//getting a list of the file names on this location
+		list := mapRef[l.Volume()].fileNamesHere(locPath)
+		// fileNamesHere() returns an empty list if no files were found
+		return list, nil
+	}
+	//if the volume has nothing on it, return an empty list as well
+	return make([]string, 0), nil
+}
+
+//ListByPrefix tags a prefix onto the current path and in a slice,
+//returns all file base names whose full paths contain that substring
+//Returns empty slice if nothing found
+func (l *Location) ListByPrefix(prefix string) ([]string, error) {
+
+	list := make([]string, 0)
+	str := path.Join(l.Path(), prefix)
+	mapRef := l.fileSystem.fsMap
+	if _, ok := mapRef[l.volume]; ok {
+		paths := mapRef[l.volume].getKeys()
+		for i := range paths {
+			if strings.Contains(paths[i], str) {
+				if path.Ext(paths[i]) != "" && strings.Contains(str, utils.EnsureTrailingSlash(path.Dir(paths[i]))) {
+					list = append(list, path.Base(paths[i]))
+					sort.Strings(list)
+				}
+			}
+		}
+	}
+
+	return list, nil
+}
+
+//ListByRegex takes a regular expression and returns
+//a slice of strings containing the base names of files
+//found that matched the regular expression.  Returns an
+//empty slice upon nothing found
+func (l *Location) ListByRegex(regex *regexp.Regexp) ([]string, error) {
+
+	list := make([]string, 0)
+	str := l.Path()
+	mapRef := l.fileSystem.fsMap
+	if _, ok := mapRef[l.Volume()]; ok {
+		namesHere := mapRef[l.Volume()].fileNamesHere(str)
+		for i := range namesHere {
+			if regex.MatchString(namesHere[i]) {
+				list = append(list, namesHere[i])
+			}
+		}
+	}
+	return list, nil
+}
+
+//Volume returns the volume of the current FileSystem.
+func (l *Location) Volume() string {
+	return l.volume
+}
+
+//Path returns the full, absolute path of the location with leading and trailing slashes
+func (l *Location) Path() string {
+
+	//just to be sure that we return a trailing and leading slash
+	str := utils.EnsureTrailingSlash(path.Clean(l.name))
+	return utils.EnsureLeadingSlash(str)
+
+}
+
+//Exists always returns true on locations
+func (l *Location) Exists() (bool, error) {
+
+	l.exists = true
+	return true, nil
+}
+
+//NewLocation creates a new location at the
+//given relative path, which is tagged onto the current locations absolute path
+func (l *Location) NewLocation(relLocPath string) (vfs.Location, error) {
+	err := utils.ValidateRelativeLocationPath(relLocPath)
+	if err != nil {
+		return nil, err
+	}
+	str := path.Join(l.Path(), relLocPath)
+	str = utils.EnsureTrailingSlash(path.Clean(str))
+	mapRef := l.fileSystem.fsMap
+	//if the location already exists on the map, just return that one
+	if object, ok := mapRef[l.volume]; ok {
+		paths := object.getKeys()
+		for _, potentialPath := range paths {
+			if ok := potentialPath == str; ok {
+				return mapRef[l.volume][potentialPath].i.(*Location), nil
+			}
+		}
+
+	}
+	return &Location{
+		fileSystem: l.fileSystem,
+		name:       str,
+		exists:     false,
+		volume:     l.Volume(),
+	}, nil
+
+}
+
+//ChangeDir simply changes the directory of the location
+func (l *Location) ChangeDir(relLocPath string) error {
+	err := utils.ValidateRelativeLocationPath(relLocPath)
+	if err != nil {
+		return err
+	}
+	l.name = path.Join(l.name, relLocPath)
+	return nil
+
+}
+
+//FileSystem returns the type of file system location exists on, if it exists at all
+func (l *Location) FileSystem() vfs.FileSystem {
+
+	return l.fileSystem
+
+}
+
+//NewFile creates a vfs.File given its relative path and tags it onto "l's" path
+func (l *Location) NewFile(relFilePath string) (vfs.File, error) {
+
+	if relFilePath == "" {
+		return nil, errors.New("cannot use empty name for file")
+	}
+	err := utils.ValidateRelativeFilePath(relFilePath)
+	if err != nil {
+		return nil, err
+	}
+
+	//after validating the path, we check to see if the
+	//file already exists. if it does, return a reference to it
+	mapRef := l.fileSystem.fsMap
+	if _, ok := mapRef[l.volume]; ok {
+		fileList := mapRef[l.volume].filesHere(l.Path())
+		for _, file := range fileList {
+			if file.name == path.Base(relFilePath) {
+				fileCopy := deepCopy(file)
+				return fileCopy, nil
+			}
+		}
+	}
+
+	//since the file didn't already exist, we will create a
+	//location and the file (NewLocation takes care of duplicates)
+	pref := l.Path()
+	str := relFilePath
+	nameStr := path.Join(pref, str)
+
+	loc, err := l.fileSystem.NewLocation(l.Volume(), utils.EnsureTrailingSlash(path.Dir(nameStr)))
+	if err != nil {
+		return nil, err
+	}
+	file := &File{
+		name: path.Base(nameStr),
+	}
+	file.memFile = newMemFile(file, loc)
+	return file, nil
+}
+
+//DeleteFile locates the file given the fileName and calls delete on it
+func (l *Location) DeleteFile(relFilePath string) error {
+	l.fileSystem.Lock()
+	defer l.fileSystem.Unlock()
+	err := utils.ValidateRelativeFilePath(relFilePath)
+	if err != nil {
+		return err
+	}
+	vol := l.Volume()
+	fullPath := path.Join(l.Path(), relFilePath)
+	mapRef := l.fileSystem.fsMap
+	if _, ok := mapRef[vol]; ok {
+		if thisObj, ok2 := mapRef[vol][fullPath]; ok2 {
+			file := thisObj.i.(*memFile)
+			file.exists = false
+			file = nil
+			thisObj.i = nil
+			thisObj = nil
+			mapRef[vol][fullPath] = nil //setting that key to nil so it truly no longer lives on this system
+			return nil
+		}
+	}
+	return errors.New("this file does not exist")
+}
+
+//URI returns the URI of the location if the location exists
+func (l *Location) URI() string {
+
+	return utils.GetLocationURI(l)
+}
diff --git a/backend/os/file.go b/backend/os/file.go
index 3af47bc..af06a7a 100644
--- a/backend/os/file.go
+++ b/backend/os/file.go
@@ -1,6 +1,7 @@
 package os
 
 import (
+	"errors"
 	"fmt"
 	"os"
 	"path"
@@ -92,6 +93,13 @@ func (f *File) Read(p []byte) (int, error) {
 // the file, 1 means relative to the current offset, and 2 means relative to the end.  It returns the new offset and
 // an error, if any.
 func (f *File) Seek(offset int64, whence int) (int64, error) {
+
+	if exists, err := f.Exists(); !exists {
+		if err != nil {
+			return 0, err
+		}
+		return 0, errors.New("file does not exist")
+	}
 	file, err := f.openFile()
 	if err != nil {
 		return 0, err
diff --git a/docs/mem.md b/docs/mem.md
new file mode 100644
index 0000000..67ccb6c
--- /dev/null
+++ b/docs/mem.md
@@ -0,0 +1,358 @@
+ mem
+--
+Package mem, in-memory backend VFS implementation. 
+## Usage 
+Rely on github.com/c2fo/vfs/backend
+
+    import(
+        "github.com/c2fo/vfs/backend"
+        "github.com/c2fo/vfs/backend/mem"
+    )
+    func UseFs() error {
+        fs, err := backend.Backend(mem.Scheme)
+        ...
+    }
+
+Or call directly:
+
+      import _mem "github.com/c2fo/vfs/backend/mem"
+      
+      func DoSomething() {
+    	fs := _mem.NewFileSystem()
+          ...
+      }
+
+## Usage
+
+```go
+const Scheme = "mem"
+```
+Scheme defines the FileSystem type's underlying implementation.
+
+#### type File
+
+```go
+type File struct {
+}
+```
+
+File implements vfs.File interface for the in-memory implementation of
+FileSystem. A file struct holds a pointer to a single memFile. Multiple threads
+will refer to the same memFile. Simultaneous reading is allowed, but writing and
+closing are protected by locks.
+
+#### func (*File) Close
+
+```go
+func (f *File) Close() error
+```
+Close imitates io.Closer by resetting the cursor and setting a boolean
+
+#### func (*File) CopyToFile
+
+```go
+func (f *File) CopyToFile(target vfs.File) error
+```
+CopyToFile copies the receiver file into the target file. The target file is
+deleted, so any references to it will be nil. In order to access the target
+after calling CopyToFile use its previous path to call it using the fsMap.
+Additionally, after this is called, f's cursor will reset as if it had been
+closed.
+
+#### func (*File) CopyToLocation
+
+```go
+func (f *File) CopyToLocation(location vfs.Location) (vfs.File, error)
+```
+CopyToLocation copies the current file to the given location. If file exists at
+given location contents are simply overwritten using "CopyToFile", otherwise a
+newFile is made, takes the contents of the current file, and ends up at the
+given location
+
+#### func (*File) Delete
+
+```go
+func (f *File) Delete() error
+```
+Delete removes the file from the FileSystem. Sets it path in the fsMap to nil,
+and also nils the file's members
+
+#### func (*File) Exists
+
+```go
+func (f *File) Exists() (bool, error)
+```
+Exists returns whether or not a file exists. Creating a file does not guarantee
+its existence, but creating one and writing to it does
+
+#### func (*File) LastModified
+
+```go
+func (f *File) LastModified() (*time.Time, error)
+```
+LastModified simply returns the file's lastModified, if the file exists
+
+#### func (*File) Location
+
+```go
+func (f *File) Location() vfs.Location
+```
+Location simply returns the file's underlying location struct pointer
+
+#### func (*File) MoveToFile
+
+```go
+func (f *File) MoveToFile(file vfs.File) error
+```
+MoveToFile creates a newFile, and moves it to "file". The receiver is always
+deleted (since it's being "moved")
+
+#### func (*File) MoveToLocation
+
+```go
+func (f *File) MoveToLocation(location vfs.Location) (vfs.File, error)
+```
+MoveToLocation moves the receiver file to the passed in location. It does so by
+creating a copy of 'f' in "location". 'f' is subsequently deleted
+
+#### func (*File) Name
+
+```go
+func (f *File) Name() string
+```
+Name returns the basename of the file
+
+#### func (*File) Path
+
+```go
+func (f *File) Path() string
+```
+Path returns the absolute path to the file
+
+#### func (*File) Read
+
+```go
+func (f *File) Read(p []byte) (n int, err error)
+```
+Read implements the io.Reader interface. Returns number of bytes read and
+potential errors
+
+#### func (*File) Seek
+
+```go
+func (f *File) Seek(offset int64, whence int) (int64, error)
+```
+Seek implements the io.Seeker interface. Returns the current position of the
+cursor and errors if any
+
+#### func (*File) Size
+
+```go
+func (f *File) Size() (uint64, error)
+```
+Size returns the size of the file contents. In our case, the length of the
+file's byte slice
+
+#### func (*File) String
+
+```go
+func (f *File) String() string
+```
+String implements the io.Stringer interface. It returns a string representation
+of the file's URI
+
+#### func (*File) Touch
+
+```go
+func (f *File) Touch() error
+```
+Touch takes a in-memory vfs.File, makes it existent, and updates the
+lastModified
+
+#### func (*File) URI
+
+```go
+func (f *File) URI() string
+```
+URI returns the file's URI, if it exists
+
+#### func (*File) Write
+
+```go
+func (f *File) Write(p []byte) (int, error)
+```
+Write implements the io.Writer interface. Returns number of bytes written and
+any errors
+
+#### type FileSystem
+
+```go
+type FileSystem struct {
+	sync.Mutex
+}
+```
+
+FileSystem implements vfs.FileSystem for an in-memory file system.
+
+#### func  NewFileSystem
+
+
+```go
+func NewFileSystem() *FileSystem
+```
+NewFileSystem is used to initialize the file system struct for an in-memory FileSystem.
+
+
+#### func (*FileSystem) Name
+
+```go
+func (fs *FileSystem) Name() string
+```
+Name returns the name of the underlying FileSystem
+
+#### func (*FileSystem) NewFile
+
+```go
+func (fs *FileSystem) NewFile(volume string, absFilePath string) (vfs.File, error)
+```
+NewFile function returns the in-memory implementation of vfs.File. Since this is
+inside FileSystem, we assume that the caller knows that the CWD is the root. If
+a non-absolute path is given, an error is thrown. Additionally, a file does not
+technically exist until a call to "Touch()" is made on it. The "Touch" call
+links the file with FileSystem's map and brings it into existence. If a file is
+written to before a touch call, Write() will take care of that call. This is
+true for other functions as well and existence only poses a problem in the
+context of deletion or copying FROM a non-existent file.
+
+#### func (*FileSystem) NewLocation
+
+```go
+func (fs *FileSystem) NewLocation(volume string, absLocPath string) (vfs.Location, error)
+```
+NewLocation function returns the in-memory implementation of vfs.Location. A
+location always exists. If a file is created on a location that has not yet been
+made in the fsMap, then the location will be created with the file
+
+#### func (*FileSystem) Retry
+
+```go
+func (fs *FileSystem) Retry() vfs.Retry
+```
+Retry will return a retrier provided via options, or a no-op if none is
+provided.
+
+#### func (*FileSystem) Scheme
+
+```go
+func (fs *FileSystem) Scheme() string
+```
+Scheme returns the scheme of the underlying FileSystem
+
+#### type Location
+
+```go
+type Location struct {
+}
+```
+
+Location implements the vfs.Location interface specific to in-memory FileSystem.
+
+#### func (*Location) ChangeDir
+
+```go
+func (l *Location) ChangeDir(relLocPath string) error
+```
+ChangeDir simply changes the directory of the location
+
+#### func (*Location) DeleteFile
+
+```go
+func (l *Location) DeleteFile(relFilePath string) error
+```
+DeleteFile locates the file given the fileName and calls delete on it
+
+#### func (*Location) Exists
+
+```go
+func (l *Location) Exists() (bool, error)
+```
+Exists always returns true on locations
+
+#### func (*Location) FileSystem
+
+```go
+func (l *Location) FileSystem() vfs.FileSystem
+```
+FileSystem returns the type of file system location exists on, if it exists at
+all
+
+#### func (*Location) List
+
+```go
+func (l *Location) List() ([]string, error)
+```
+List finds all of the files living at the current location and returns them in a
+slice of strings. If there are no files at location, then an empty slice will be
+returned
+
+#### func (*Location) ListByPrefix
+
+```go
+func (l *Location) ListByPrefix(prefix string) ([]string, error)
+```
+ListByPrefix tags a prefix onto the current path and in a slice, returns all
+file base names whose full paths contain that substring Returns empty slice if
+nothing found
+
+#### func (*Location) ListByRegex
+
+```go
+func (l *Location) ListByRegex(regex *regexp.Regexp) ([]string, error)
+```
+ListByRegex takes a regular expression and returns a slice of strings containing
+the base names of files found that matched the regular expression. Returns an
+empty slice upon nothing found
+
+#### func (*Location) NewFile
+
+```go
+func (l *Location) NewFile(relFilePath string) (vfs.File, error)
+```
+NewFile creates a vfs.File given its relative path and tags it onto "l's" path
+
+#### func (*Location) NewLocation
+
+```go
+func (l *Location) NewLocation(relLocPath string) (vfs.Location, error)
+```
+NewLocation creates a new location at the given relative path, which is tagged
+onto the current locations absolute path
+
+#### func (*Location) Path
+
+```go
+func (l *Location) Path() string
+```
+Path returns the full, absolute path of the location with leading and trailing
+slashes
+
+#### func (*Location) String
+
+```go
+func (l *Location) String() string
+```
+String implements io.Stringer by returning the location's URI as a string
+
+#### func (*Location) URI
+
+```go
+func (l *Location) URI() string
+```
+URI returns the URI of the location if the location exists
+
+#### func (*Location) Volume
+
+```go
+func (l *Location) Volume() string
+```
+Volume returns the volume of the current FileSystem.
diff --git a/go.sum b/go.sum
index 183cc1e..7aa6d0d 100644
--- a/go.sum
+++ b/go.sum
@@ -10,12 +10,15 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/googleapis/gax-go v2.0.2+incompatible h1:silFMLAnr330+NRuag/VjIGF7TLp/LBrV2CJKFLWEww=
 github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
diff --git a/utils/utils.go b/utils/utils.go
index 4126e9e..c3457a0 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -48,7 +48,7 @@ func ValidateAbsoluteFilePath(name string) error {
 
 // ValidateRelativeFilePath ensures that a file path has neither leading nor trailing slashes
 func ValidateRelativeFilePath(name string) error {
-	if strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") {
+	if name == "" || strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") {
 		return errors.New(ErrBadRelFilePath)
 	}
 	return nil


Test patch:
diff --git a/backend/mem/file_test.go b/backend/mem/file_test.go
new file mode 100644
index 0000000..3d9485a
--- /dev/null
+++ b/backend/mem/file_test.go
@@ -0,0 +1,730 @@
+package mem
+
+import (
+	"io/ioutil"
+	"log"
+	"os"
+	"path"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/c2fo/vfs/v5"
+	"github.com/c2fo/vfs/v5/backend"
+	_os "github.com/c2fo/vfs/v5/backend/os"
+	"github.com/stretchr/testify/suite"
+)
+
+/**********************************
+ ************TESTS*****************
+ **********************************/
+
+type memFileTest struct {
+	suite.Suite
+	testFile   *File
+	fileSystem *FileSystem
+}
+
+func (s *memFileTest) SetupTest() {
+	fs := NewFileSystem()
+	file, nerr := fs.NewFile("C", "/test_files/test.txt")
+	s.NoError(nerr, "unexpected error creating file")
+
+	//initializing our test file. casting it, and bringing it into existence by calling Touch() on it
+	s.testFile = file.(*File)
+	s.fileSystem = fs
+	s.NoError(s.testFile.Touch(), "unexpected error touching file")
+}
+
+func (s *memFileTest) TeardownTest() {
+	err := s.testFile.Close()
+	s.NoError(err, "close error not expected")
+	s.NoError(s.testFile.Delete(), "delete failed unexpectedly")
+}
+
+//TestZBR ensures that we can always read zero bytes
+func (s *memFileTest) TestZBR() {
+
+	byteSlice := make([]byte, 0)
+	_, err := s.testFile.Read(byteSlice)
+	s.NoError(err, "read of 0 bytes failed")
+
+}
+
+//TestRARO ensures that read after read w/file still open (and without a seek) will incur an error
+func (s *memFileTest) TestRARO() {
+	sliceToWrite := "hello world!"
+	byteSlice := make([]byte, len(sliceToWrite))
+	byteSlice2 := make([]byte, len(sliceToWrite))
+
+	_, err := s.testFile.Write([]byte(sliceToWrite)) //writing our bytes to the buffer so we have something to read
+	s.NoError(err, "unexpected write error")
+	s.NoError(s.testFile.Close(), "unexpected error closing file") //closing the file so the buffer contents are stored into "contents"
+
+	_, err = s.testFile.Read(byteSlice) //initial read
+	s.NoError(err, "unexpected read error")
+	_, err = s.testFile.Read(byteSlice2)
+	//an error should occur here since the first read
+	//moved the cursor all the way through and we did
+	//not close the file before reading again
+	s.Error(err, "read was expected to fail")
+}
+
+// TestRARC tests a read after read w/file closed between reads to see ensure an error occurs
+func (s *memFileTest) TestRARC() {
+	sliceToWrite := "hello world!"
+	byteSlice := make([]byte, len(sliceToWrite))
+	byteSlice2 := make([]byte, len(sliceToWrite))
+
+	_, err := s.testFile.Write([]byte(sliceToWrite)) //Writing our bytes to the buffer so we have something to read.
+	s.NoError(err, "unexpected write error")
+	s.NoError(s.testFile.Close(), "unexpected error closing file") //Closing the file so the buffer contents are stored into "contents".
+
+	//This is the initial read.
+	_, err = s.testFile.Read(byteSlice)
+	s.NoError(err, "unexpected read error")
+	s.NoError(s.testFile.Close(), "unexpected error closing file")
+	_, err = s.testFile.Read(byteSlice2)
+	s.NoError(err, "unexpected read error")
+
+	//No error should occur here since the first read
+	//moved the cursor all the way through but we closed
+	//the file before reading again, so it should reset it.
+	s.NoError(err, "read after read failed!")
+	s.Equal(byteSlice, byteSlice2)
+
+}
+
+//TestNewFileSameName creates two files with the same name and ensures
+//that the second creation returns a reference to the first
+func (s *memFileTest) TestNewFileSameName() {
+	sharedPath := "/path/to/file.txt"
+	firstFile, err := s.fileSystem.NewFile("", sharedPath)
+	s.NoError(err, "unexpected error creating a file")
+	expectedText := "hey y'all!"
+	_, err = firstFile.Write([]byte(expectedText))
+	s.NoError(err, "unexpected error writing to file")
+	secondFile, err := s.fileSystem.NewFile("", sharedPath)
+	s.NoError(err, "unexpected error creating a file")
+	expectedSlice := make([]byte, len(expectedText))
+	//since secondFile references firstFile, reading will throw an error as we never closed or seeked firstFile
+	_, err = secondFile.Read(expectedSlice)
+	s.Error(err, "expected read error since firstFile was never closed")
+	//after this call, we can expect to be able to read from secondFile since its reference, firstFile, was closed
+	s.NoError(firstFile.Close(), "unexpected error closing file")
+	_, err = secondFile.Read(expectedSlice)
+	s.NoError(err, "unexpected read error")
+	s.Equal(expectedText, string(expectedSlice))
+}
+
+//TestDelete deletes the receiver file, then creates another file and deletes it.
+//Succeeds only on both successful deletions
+func (s *memFileTest) TestDelete() {
+
+	newFile, err := s.fileSystem.NewFile("", "/home/bar.txt")
+	s.NoError(err, "unexpected creation error")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	otherFile, err := s.fileSystem.NewFile("", "/foo.txt")
+	s.NoError(err, "unexpected creation error")
+	s.NoError(otherFile.Touch(), "unexpected error touching file")
+	existence, err := otherFile.Exists()
+	s.True(existence)
+	s.NoError(err, "unexpected existence error")
+
+	//deleting otherFile and ensuring it no longer exists
+	s.NoError(otherFile.Delete(), "Delete unexpectedly failed")
+	existence1, err := otherFile.Exists()
+	s.False(existence1)
+	s.NoError(err, "unexpected existence error")
+}
+
+//TestExists1 uses "Exists()" to check for existence of our receiver's file then creates a file and does the same thing.
+func (s *memFileTest) TestExists1() {
+	doesExist, err := s.testFile.Exists()
+	s.NoError(err, "unexpected existence error")
+	s.True(doesExist) //double check that the error message was right
+
+	otherFile, err := s.fileSystem.NewFile("", "/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(otherFile.Touch(), "unexpected error touching file")
+	doesExist1, err := otherFile.Exists()
+	s.NoError(err, "unexpected existence error")
+	s.True(doesExist1)
+}
+
+//TestExists2 ensures that "Exists()" detects a non-existent file
+func (s *memFileTest) TestExists2() {
+	otherFile, err := s.fileSystem.NewFile("", "/test_file/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(otherFile.Touch(), "unexpected error touching file")
+	//deleting otherFile and asserting non-existence
+	s.NoError(otherFile.Delete(), "delete unexpectedly failed")
+	existence, err := otherFile.Exists()
+	s.NoError(err, "unexpected existence error")
+	s.False(existence)
+}
+
+//TestNewFile creates a file and then checks that it exists in our system the the path expected
+func (s *memFileTest) TestNewFile() {
+
+	file, err := s.fileSystem.NewFile("", "/test_file/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(file.Touch(), "unexpected error touching file")
+	filePath := file.Path()
+	returnedFile, err := s.fileSystem.NewFile(file.Location().Volume(), filePath) //checking our system map for a match to the given fileName
+	s.NoError(err, "unexpected error retrieving file")
+	s.True(returnedFile.Name() == "foo.txt") //casting the object to a file so we can call "Name()"
+
+}
+
+//TestSeek2 tests to ensure that we can seek to any part of the file, if it exists
+func (s *memFileTest) TestSeek2() {
+	newFile, err := s.fileSystem.NewFile("", "/home/test_files/subdir/seekTest.txt")
+	s.NoError(err, "unexpected error creating a new file")
+	initText := "Hello world!"
+
+	_, err = newFile.Write([]byte(initText))
+	s.NoError(err, "unexpected write error")
+
+	//without closing, the cursor should be at the end of the file.
+	//calling Seek with a whence of '1' and asking to move to cursor forward should throw an error
+	_, err = newFile.Seek(1, 1)
+	s.Error(err, "expected seek to throw an error")
+	//trying to read should also be an error:
+	_, err = newFile.Read(make([]byte, 1))
+	s.Error(err, "expected read error")
+	s.NoError(newFile.Close(), "unexpected close error")
+
+	_, err = newFile.Seek(0, 0)
+	s.NoError(err, "unexpected seek error")
+	testByte := make([]byte, 1)
+
+	num, err := newFile.Read(testByte)
+	s.NoError(err, "unexpected read error")
+	s.True("H" == string(testByte))
+	s.Equal(1, num)
+
+	_, err = newFile.Seek(-2, 1)
+	s.Error(err, "seek error expected for seeking into negative bounds")
+
+	_, err = newFile.Seek(1, 1)
+	s.NoError(err, "unexpected seek error")
+}
+
+//TestNameToURI creates a file then pulls it out of the fsMap using its name and ensures its file and location URIs are correct
+func (s *memFileTest) TestNameToURI() {
+
+	name := "/test_files/examples/foo.txt"
+	file, err := s.fileSystem.NewFile("C", name)
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(file.Touch(), "unexpected error touching file")
+	//fsMap returns an "fsObject" so to get a file we need to cast its member interface to a file
+	returnedFile, err := s.fileSystem.NewFile("C", "/test_files/examples/foo.txt")
+	s.NoError(err, "unexpected error retrieving file")
+
+	s.NoError(returnedFile.Touch(), "unexpected error touching file")
+	//checking existence in the fsMap using "retFile's" path
+	existence, err := returnedFile.Exists()
+	s.NoError(err, "unexpected existence error")
+	s.True(existence)
+	s.Equal("mem://C/test_files/examples/", returnedFile.Location().URI())
+	s.Equal("mem://C/test_files/examples/foo.txt", returnedFile.URI())
+
+}
+
+//TestOpenFile ensures that reading after a write without seeking or closing a file throws an error
+func (s *memFileTest) TestOpenFile() {
+	expectedText := "hello world"
+
+	_, err := s.testFile.Write([]byte(expectedText))
+	data := make([]byte, len(expectedText))
+	s.NoError(err, "write error not expected")
+
+	_, err = s.testFile.Read(data)
+	s.Error(err, "read error expected")
+	s.False(expectedText == string(data))
+}
+
+//TestSeek writes to a file and seeks to the beginning of it to read what it wrote
+func (s *memFileTest) TestSeek() {
+	expectedText := "new file"
+	data := make([]byte, len(expectedText))
+	file, err := s.fileSystem.NewFile("", "/test_files/new.txt")
+	s.NoError(err, "unexpected error creating a file")
+	_, err = file.Write([]byte(expectedText))
+	s.NoError(err, "write error not expected")
+
+	_, err = file.Seek(0, 0)
+	s.NoError(err, "seek error not expected")
+	s.NoError(file.Close(), "unexpected close error")
+	_, err = file.Read(data)
+	s.NoError(err, "read error not expected")
+	s.NoError(file.Close(), "close error not expected")
+
+	s.Equal(expectedText, string(data))
+
+	found, err := file.Exists()
+	s.NoError(err, "exists error not expected")
+	s.True(found)
+
+	s.NoError(file.Delete(), "file was not deleted properly")
+
+	found2, err := file.Exists()
+	s.NoError(err, "exists error not expected")
+	s.False(found2)
+
+}
+
+//TestCopyToLocation copies a file to a location that has
+//been passed in. Succeeds on existence of original file and its
+//contents in new location
+func (s *memFileTest) TestCopyToLocation() {
+
+	newFile, err := s.fileSystem.NewFile("", "/home/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	expectedText := "hello world!"
+	_, err = s.testFile.Write([]byte(expectedText))
+	s.NoError(err, "unexpected write error")
+
+	s.NoError(s.testFile.Close(), "unexpected close error")
+
+	readSlice1 := make([]byte, len(expectedText))
+	readSlice2 := make([]byte, len(expectedText))
+
+	copiedFile, cerr := s.testFile.CopyToLocation(newFile.Location())
+	s.NoError(cerr, "CopyToLocation unexpectedly failed")
+
+	s.NoError(copiedFile.Touch(), "unexpected error touching file")
+	s.True(copiedFile != nil)
+	//making sure the path was correctly updated
+	s.EqualValues("/home/test.txt", copiedFile.Path())
+
+	_, err = copiedFile.Read(readSlice1)
+	s.NoError(err, "unexpected read error")
+
+	_, err = s.testFile.Read(readSlice2)
+	s.NoError(err, "unexpected read error")
+	s.EqualValues(string(readSlice2), string(readSlice1))
+
+}
+
+//TestCopyToLocationOW copies a file to a location that has
+//a file with the same name. Succeeds only on overwrite of file
+//in specified location
+func (s *memFileTest) TestCopyToLocationOW() {
+
+	newFile, err := s.fileSystem.NewFile("C", "/home/test.txt")
+	s.NoError(err, "unexpected error creating a file")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	originalText := "goodbye world!"
+	_, err = newFile.Write([]byte(originalText))
+	s.NoError(err, "unexpected write error")
+	s.NoError(newFile.Close(), "unexpected Close error")
+
+	expectedText := "hello world!"
+	_, err = s.testFile.Write([]byte(expectedText))
+	s.NoError(err, "unexpected write error")
+	s.NoError(s.testFile.Close(), "unexpected Close error")
+
+	readSlice := make([]byte, len(expectedText))
+	copiedFile, err := s.testFile.CopyToLocation(newFile.Location())
+	s.NoError(err, "CopyToLocation unexpectedly failed")
+	s.True(copiedFile != nil)
+	s.NoError(copiedFile.Close(), "unexpected close error")
+
+	s.EqualValues("/home/test.txt", copiedFile.Path())
+	_, err = copiedFile.Read(readSlice)
+	s.NoError(err, "unexpected read error")
+	s.EqualValues("hello world!", string(readSlice))
+
+}
+
+//TestCopyToNilFile ensures that an error is thrown for trying to copy to a nil file
+func (s *memFileTest) TestCopyToNilFile() {
+
+	newFile, err := s.fileSystem.NewFile("", "/nilFile.txt")
+	s.NoError(err, "unexpected error creating file")
+	newFile = nil
+	err = s.testFile.CopyToFile(newFile)
+	s.Error(err, "expected error for copying to nil file")
+}
+
+//TestCopyToLocationOS copies a file from the in-memory FileSystem over
+// to a location on the  OS FileSystem and checks for success
+func (s *memFileTest) TestCopyToLocationOS() {
+
+	expectedText := "hello world!"
+	_, err := s.testFile.Write([]byte(expectedText))
+	s.NoError(err, "unexpected write error")
+	s.NoError(s.testFile.Close(), "unexpected error closing file")
+
+	var osFile vfs.File
+	dir, err := ioutil.TempDir("", "osDir")
+	if err != nil {
+		log.Fatal(err)
+	}
+	osFileName := filepath.Join(dir, "osFile.txt")
+
+	osFile, err = backend.Backend(_os.Scheme).NewFile("", osFileName)
+	s.NoError(err, "unexpected error creating osFile")
+	_, err = osFile.Write(make([]byte, 0))
+	s.NoError(err, "unexpected error writing zero bytes to osFile")
+
+	exists, err := osFile.Exists()
+	s.NoError(err, "unexpected existence error")
+	s.True(exists)
+
+	readSlice := make([]byte, len(expectedText))
+	readSlice2 := make([]byte, len(expectedText))
+
+	copiedFile, err := s.testFile.CopyToLocation(osFile.Location())
+	s.NoError(err, "CopyToLocation unexpectedly failed")
+	s.NoError(copiedFile.Close(), "unexpected Close error")
+
+	s.True(copiedFile != nil)
+	s.EqualValues("/test_files/test.txt", s.testFile.Path())         //testFile's path should be unchanged
+	s.EqualValues(filepath.Join(dir, "test.txt"), copiedFile.Path()) //new path should be that
+
+	_, err = copiedFile.Read(readSlice)
+	s.NoError(err, "unexpected read error")
+
+	_, err = s.testFile.Read(readSlice2)
+	s.NoError(err, "unexpected read error")
+	s.EqualValues(string(readSlice2), string(readSlice)) //both reads should be the same
+	cleanErr := os.RemoveAll(dir)                        // clean up
+	s.NoError(cleanErr, "unexpected error cleaning up osFiles")
+}
+
+//TestCopyToFile tests "CopyToFile()" between two files both in the in-memory FileSystem
+func (s *memFileTest) TestCopyToFile() {
+
+	expectedText := "hello world"
+	otherFile, err := s.fileSystem.NewFile("", "/test.txt")
+	s.NoError(err, "unexpected error creating file")
+	readSlice1 := make([]byte, len(expectedText))
+	readSlice2 := make([]byte, len(expectedText))
+	num, err := s.testFile.Write([]byte(expectedText))
+	s.False(num == 0)
+	s.NoError(err, "no error expected from Write but got one")
+	s.NoError(s.testFile.Close(), "unexpected error closing a file")
+
+	s.NoError(otherFile.Touch(), "unexpected error touching file")
+	err = s.testFile.CopyToFile(otherFile)
+	s.NoError(err, "copy to file failed unexpectedly")
+
+	_, err = s.testFile.Read(readSlice1)
+	s.NoError(err, "unexpected read error")
+	otherFile, err = s.fileSystem.NewFile("", "/test.txt")
+	s.NoError(err, "unexpected creation error")
+	_, err = otherFile.Read(readSlice2)
+	s.NoError(err, "unexpected read error")
+	s.EqualValues(string(readSlice1), string(readSlice2))
+
+}
+
+//TestCopyToFileOS tests "CopyToFile()" between one file in the in-memory FileSystem and the other in the os FileSystem
+func (s *memFileTest) TestCopyToFileOS() {
+
+	expectedText := "Hello World!"
+	var osFile vfs.File
+	var err error
+	dir, err := ioutil.TempDir("", "osDir")
+	if err != nil {
+		log.Fatal(err)
+	}
+	osFileName := filepath.Join(dir, "osFile.txt")
+	osFile, err = backend.Backend(_os.Scheme).NewFile("", osFileName)
+	s.NoError(err, "unexpected error creating osFile")
+	_, err = osFile.Write(make([]byte, 0))
+	s.NoError(err, "unexpected error writing zero bytes to osFile")
+	num, err := s.testFile.Write([]byte(expectedText))
+	s.False(num == 0)
+	s.NoError(err, "no error expected from Write but got one")
+	s.NoError(s.testFile.Close(), "unexpected error closing a  file")
+
+	err = s.testFile.CopyToFile(osFile)
+	s.NoError(err, "copy to file failed unexpectedly")
+	s.NoError(osFile.Close(), "unexpected error closing a  file")
+
+	size1, err := s.testFile.Size()
+	s.NoError(err, "unexpected error retrieving size")
+	size2, err := osFile.Size()
+	s.NoError(err, "unexpected error retrieving size")
+	s.Equal(size1, size2)
+
+}
+
+//TestEmptyCopy to file creates two files,
+//one is empty the other is not. Calls
+//CopyToFile using the empty one on t
+//he non-empty one. Succeeds on the non-empty
+//file becoming empty
+func (s *memFileTest) TestEmptyCopyToFile() {
+
+	expectedText := ""
+	expectedSlice := make([]byte, 0)
+	otherFile, err := s.fileSystem.NewFile("", "/some/path/otherfile.txt")
+
+	s.NoError(err, "unexpected error creating a file")
+
+	_, err = otherFile.Write([]byte("yooooooooooo"))
+	s.NoError(err, "unexpected Write error")
+	s.NoError(otherFile.Close(), "unexpected close error")
+
+	emptyFile, nerr := s.fileSystem.NewFile("C", "/test_files/empty.txt")
+	s.NoError(nerr, "file creation was not successful so it does not exist")
+	_, err = emptyFile.Write([]byte(""))
+	s.NoError(err, "unexpected Write error")
+
+	s.NoError(emptyFile.Close(), "unexpected close error")
+	//call to CopyToFile
+	s.NoError(emptyFile.CopyToFile(otherFile), "CopyToFile failed unexpectedly")
+
+	_, err = otherFile.Read(expectedSlice)
+	s.NoError(err, "unexpected Read error")
+	s.Equal(expectedText, string(expectedSlice))
+
+}
+
+//TestMoveToLocation ensures that we can move files to specified locations
+func (s *memFileTest) TestMoveToLocation() {
+
+	newFile, err := s.fileSystem.NewFile("", "/otherDir/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	str1 := newFile.Path()
+	file, err := newFile.MoveToLocation(s.testFile.Location())
+	s.NoError(err, "unexpected move to location error")
+
+	exists, err := newFile.Exists() //newFile shouldn't exist since MovetoLocation deletes the calling object
+	s.NoError(err, "unexpected existence error")
+	s.False(exists)
+
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	str2 := file.Path()
+	s.Equal(path.Base(str1), path.Base(str2))
+	s.Equal("/test_files/", file.Location().Path())
+
+}
+
+//TestMoveToLocation2 creates two files with the same name in different places and moves one to the other
+func (s *memFileTest) TestMoveToLocation2() {
+
+	expectedText := "Who ya calling pinhead?"
+	newFile, err := s.fileSystem.NewFile("", "/otherDir/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+
+	otherFile, err := s.fileSystem.NewFile("", "/thisDir/foo.txt")
+	s.NoError(err, "unexpected error creating file")
+	_, err = otherFile.Write([]byte(expectedText))
+	s.NoError(err, "unexpected write error")
+	s.NoError(otherFile.Close(), "unexpected close error")
+	str1 := newFile.Path()
+	file, _ := otherFile.MoveToLocation(newFile.Location())
+	str2 := file.Path()
+	s.Equal(path.Base(str1), path.Base(str2))
+
+	exists, _ := otherFile.Exists()
+	s.False(exists)
+	readSlice := make([]byte, len(expectedText))
+
+	_, err = file.Read(readSlice)
+	s.NoError(err, "unexpected read error")
+	s.Equal(expectedText, string(readSlice))
+
+}
+
+//TestMoveToFile creates a newFile and moves the testFile to it.
+//Test succeeds if the moved file has the correct data.  They share the same name, so this is effectively a "CopyToFile" call
+func (s *memFileTest) TestMoveToFile() {
+
+	expectedSlice := []byte("Hello World!")
+	newFile, err := s.fileSystem.NewFile("", "/samples/test.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+
+	_, err = s.testFile.Write(expectedSlice)
+	s.NoError(err, "Write failed unexpectedly")
+	s.NoError(s.testFile.Close(), "unexpected close error")
+
+	//after this call, newFile and "s.testFile" will be deleted.
+	//we re-obtain the newFile pointer by calling it from our fsMap by giving it the (new) path and volume
+	err = s.testFile.MoveToFile(newFile)
+	s.NoError(err, "move to file failed")
+	newFileSlice := make([]byte, len("Hello World!"))
+	newFile, err = s.fileSystem.NewFile("", "/samples/test.txt")
+	s.NoError(err, "unexpected creation error")
+
+	s.False(s.testFile.Exists())
+
+	_, err = newFile.Read(newFileSlice)
+	s.NoError(err, "read unexpectedly failed")
+
+	s.Equal(string(expectedSlice), string(newFileSlice))
+	s.Equal("/samples/test.txt", newFile.Path())
+
+}
+
+//TestMoveToFile2 ensures that if a call is made on a files who don't share the same name, a new file is produced with the caller's name
+func (s *memFileTest) TestMoveToFile2() {
+
+	expectedSlice := []byte("Hello World!")
+	newFile, err := s.fileSystem.NewFile("", "/samples/diffName.txt")
+	s.NoError(err, "file creation was not successful so it does not exist")
+	//newPath := "/samples/test.txt" //this is the path used to retrieve the file after it has been moved
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	_, err = s.testFile.Write(expectedSlice)
+	s.NoError(err, "write failed unexpectedly")
+	s.NoError(s.testFile.Close(), "unexpected close error")
+
+	//after this call, newFile and "s.testFile" will be deleted.
+	//we re-obtain the newFile by calling it from our fsMap by giving it the (new) path and volume
+	err = s.testFile.MoveToFile(newFile)
+	s.NoError(err, "move to file failed")
+	newFileSlice := make([]byte, len("Hello World!"))
+	//newFile = s.fileSystem.fsMap[""][newPath].i.(*File)
+	s.False(s.testFile.Exists())
+
+	_, err = newFile.Read(newFileSlice)
+	s.NoError(err, "read unexpectedly failed")
+	s.Equal(string(expectedSlice), string(newFileSlice))
+	s.Equal("/samples/diffName.txt", newFile.Path())
+
+}
+
+//TestWrite writes a string to a file and checks for success by comparing the number of bytes
+//written by "Write()" to the length of the slice it wrote from
+func (s *memFileTest) TestWrite() {
+	expectedText := "I'm fed up with this world" //-Tommy Wiseau
+	bSlice := []byte(expectedText)
+	length := len(bSlice)
+	num, err := s.testFile.Write(bSlice)
+	s.NoError(err, "unexpected write error")
+	s.EqualValues(length, num)
+
+}
+
+//TestWriteThenReadNoClose writes to a file, and reads from it without closing it by seeking to the start
+func (s *memFileTest) TestWriteThenReadNoClose() {
+	expectedText := "new file"
+	data := make([]byte, len(expectedText))
+	file, err := s.fileSystem.NewFile("", "/test_files/new.txt")
+	s.NoError(err, "unexpected error creating a new file")
+
+	_, err = file.Write([]byte(expectedText))
+	s.NoError(err, "write error not expected")
+
+	_, err = file.Seek(0, 0)
+	s.NoError(err, "seek error not expected")
+	_, err = file.Read(data)
+	s.Error(err, "read error  expected")
+
+	s.NoError(file.Close(), "close error not expected")
+	_, err = file.Read(data)
+	s.NoError(err, "read error not expected")
+
+	s.Equal(expectedText, string(data))
+
+	found, err := file.Exists()
+	s.NoError(err, "exists error not expected")
+	s.True(found)
+
+	//deleting file and asserting non-existence
+	s.NoError(file.Delete(), "unexpected error deleting file")
+	found2, err := file.Exists()
+	s.NoError(err, "exists error not expected")
+	s.False(found2)
+}
+
+//TestLastModified Writes to a file then retrives the value that LastModified() returns and the lastModified value
+//stored in the File struct and compares them against eachother.  Successful if they are equal.
+func (s *memFileTest) TestLastModified() {
+	data := "Hello World!"
+	sliceData := []byte(data)
+	_, err := s.testFile.Write(sliceData)
+	s.NoError(err, "write did not work as expected!")
+
+	t, _ := s.testFile.LastModified()
+	firstTime := *t
+	time.Sleep(1 * time.Second)
+	_, err = s.testFile.Write([]byte("hey!"))
+	s.NoError(err, "unexpected write error")
+
+	secondTime := *t
+
+	s.True(secondTime.UnixNano() > firstTime.UnixNano())
+
+}
+
+//TestName creates a file and names it and then asserts that the given name and the return of Name() match.
+func (s *memFileTest) TestName() {
+	newFile, err := s.fileSystem.NewFile("", "/test_files/lots/of/directories/here/we/go/test.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	s.Equal("test.txt", newFile.Name())
+}
+
+//TestSize allocates 64 bytes in one file and 32 in the other.  It makes sure that Size() returns the correct sizes.
+func (s *memFileTest) TestSize() {
+	otherFile, err := s.fileSystem.NewFile("", "/test.txt")
+	s.NoError(err, "unexpected error creting file")
+	_, err = s.testFile.Write(make([]byte, 64))
+	s.NoError(err, "unexpected write error")
+	_, err = otherFile.Write(make([]byte, 32))
+	s.NoError(err, "unexpected write error")
+
+	s.NoError(s.testFile.Close(), "unexpected close error")
+	s.NoError(otherFile.Close(), "unexpected close error")
+
+	size1, err := s.testFile.Size()
+	s.NoError(err, "unexpected error retrieving size")
+	size2, err := otherFile.Size()
+	s.NoError(err, "unexpected error retrieving size")
+	s.True(size1 > size2)
+
+}
+
+//TestPath makes sure that locations return the correct paths, along with leading and trailing slashes
+func (s *memFileTest) TestPath() {
+	str1 := "/home/some/directory/test_files/test.txt"
+	_, err := s.fileSystem.NewFile("", str1)
+	s.NoError(err, "unexpected error creating a new file")
+
+	_, err = s.fileSystem.NewFile("", "/test_files/bar.txt")
+	s.NoError(err, "unexpected error creating a new file")
+
+	file1, err := s.fileSystem.NewFile("", "/directory/bar.txt")
+	s.NoError(err, "unexpected error creating a new file")
+	s.NoError(file1.Touch(), "unexpected error touching file")
+
+	str := "/directory/test_files/test.txt"
+	_, err = s.fileSystem.NewFile("", str)
+	s.NoError(err, "unexpected error creating a new file")
+	s.Equal("/directory/bar.txt", file1.Path())
+}
+
+//TestURI ensures that URI's for locations come out in the correct format
+func (s *memFileTest) TestURI() {
+	file, err := s.fileSystem.NewFile("C", "/test_files/lots/of/directories/here/we/go/test.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(file.Touch(), "unexpected error touching file")
+	uri := file.URI()
+	s.Equal("mem://C/test_files/lots/of/directories/here/we/go/test.txt", uri)
+}
+
+//TestStringer tests the implementation of io.Stringer
+func (s *memFileTest) TestStringer() {
+
+	file, err := s.fileSystem.NewFile("", "/test_files/lots/of/directories/here/we/go/test.txt")
+	s.NoError(err, "unexpected error creating file")
+	s.NoError(file.Touch(), "unexpected error touching file")
+	str := file.String()
+	s.Equal("mem:///test_files/lots/of/directories/here/we/go/test.txt", str)
+
+}
+
+func TestMemFile(t *testing.T) {
+	suite.Run(t, new(memFileTest))
+	_ = os.Remove("test_files/new.txt")
+}
diff --git a/backend/mem/location_test.go b/backend/mem/location_test.go
new file mode 100644
index 0000000..b83642d
--- /dev/null
+++ b/backend/mem/location_test.go
@@ -0,0 +1,319 @@
+package mem
+
+import (
+	"path"
+	"regexp"
+	"testing"
+
+	"github.com/c2fo/vfs/v5"
+	"github.com/stretchr/testify/suite"
+)
+
+/**********************************
+ ************TESTS*****************
+ **********************************/
+
+type memLocationTest struct {
+	suite.Suite
+	testFile   vfs.File
+	fileSystem *FileSystem
+}
+
+func (s *memLocationTest) SetupTest() {
+	fs := NewFileSystem()
+
+	file, nerr := fs.NewFile("", "/test_files/test.txt")
+	s.NoError(nerr, "file creation was not successful so it does not exist")
+
+	s.testFile = file.(*File)
+	s.fileSystem = fs
+	s.NoError(s.testFile.Touch(), "unexpected error touching file")
+}
+
+//TestFSName tests out whether or not the location knows what filesystem it is on
+func (s *memLocationTest) TestFSName() {
+
+	s.Equal(s.testFile.Location().FileSystem().Name(), "In-Memory Filesystem")
+	s.Equal(s.testFile.Location().FileSystem().Scheme(), "mem")
+}
+
+//TestList tests that a location is capable of finding all files that exist there
+func (s *memLocationTest) TestList() {
+	expected := []string{"test.txt"}
+	actual, _ := s.testFile.Location().List()
+	s.Equal(expected, actual)
+}
+
+//TestList_NonExistentDirectory is a test copied over from OS
+//that creates locations and ensures that they do not exist
+//by showing that no files live on those directories
+func (s *memLocationTest) TestList_NonExistentDirectory() {
+	location, err := s.testFile.Location().NewLocation("not/a/directory/")
+	s.Nil(err, "error isn't expected")
+
+	exists, err := location.Exists()
+	s.Nil(err, "error isn't expected")
+	s.True(exists, "location should return true for Exists")
+
+	contents, err := location.List()
+	s.Nil(err, "error isn't expected")
+	s.Equal(0, len(contents), "list should return empty slice for non-existent directory")
+
+	prefixContents, err := location.ListByPrefix("anything")
+	s.Nil(err, "error isn't expected")
+	s.Equal(0, len(prefixContents), "ListByPrefix should return empty slice for non-existent directory")
+
+	regex, _ := regexp.Compile("[-]+")
+	regexContents, err := location.ListByRegex(regex)
+	s.Nil(err, "error isn't expected")
+	s.Equal(0, len(regexContents), "ListByRegex should return empty slice for non-existent directory")
+}
+
+//TestListByPrefix creates some files and provides a prefix. Succeeds on correct string slice returned
+func (s *memLocationTest) TestListByPrefix() {
+
+	f1, nerr := s.fileSystem.NewFile("", "/foo.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.NoError(f1.Touch(), "unexpected error touching file")
+
+	f2, nerr := s.fileSystem.NewFile("", "/home/test_files/subdir/file1.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.NoError(f2.Touch(), "unexpected error touching file")
+
+	f3, nerr := s.fileSystem.NewFile("", "/home/test_files/subdir/file2.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.NoError(f3.Touch(), "unexpected error touching file")
+
+	f4, nerr := s.fileSystem.NewFile("", "/home/directories/test/mat.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.NoError(f4.Touch(), "unexpected error touching file")
+
+	f5, nerr := s.fileSystem.NewFile("", "/test/files/car.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.NoError(f5.Touch(), "unexpected error touching file")
+
+	loc, lerr := s.fileSystem.NewLocation("", "/home/test_files/subdir/")
+	s.NoError(lerr, "unexpected error creating a location")
+
+	nameSlice, lerr := loc.ListByPrefix("f")
+	s.NoError(lerr, "unexpected error obtaining list by prefix")
+	expectedSlice := []string{"file1.txt", "file2.txt"}
+	s.Equal(expectedSlice, nameSlice)
+	emptySlice, err := s.testFile.Location().ListByPrefix("m")
+	s.NoError(err, "unexpected error retrieving files by prefix")
+	s.Equal(make([]string, 0), emptySlice) //no files should be found with this prefix at this location
+}
+
+//TestListByRegex provides a simple regular expression and ensures that the correct fileNames matched that regEx
+func (s *memLocationTest) TestListByRegex() {
+
+	newFile, nerr := s.fileSystem.NewFile("", "/test_files/test.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	expected := []string{"test.txt"}
+
+	regex, err := regexp.Compile("[est]+")
+	s.NoError(err, "unexpected error compiling regEx")
+
+	actual, err := newFile.Location().ListByRegex(regex)
+	s.NoError(err, "unexpected error listing by regEx")
+
+	s.Equal(expected, actual)
+	err = newFile.Location().ChangeDir("../")
+	s.NoError(err, "unexpected error changing directories")
+
+	regex2, err := regexp.Compile("[test.txt]")
+	s.NoError(err, "unexpected error compiling regEx")
+
+	actual2, err := newFile.Location().ListByRegex(regex2)
+	s.NoError(err, "unexpected error listing by regEx")
+	s.Equal(expected, actual2)
+
+}
+
+//TestExists ensures that a real location exists, and one that was simply created does not
+func (s *memLocationTest) TestExists() {
+	otherFile, nerr := s.fileSystem.NewFile("", "/foo/foo.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.True(s.testFile.Location().Exists())
+	s.True(otherFile.Location().Exists())
+}
+
+//TestNewLocation ensures that we can create new locations, even with relative dot paths
+func (s *memLocationTest) TestNewLocation() {
+
+	otherFile, nerr := s.fileSystem.NewFile("", "/foo/foo.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	fileLocation := otherFile.Location()
+	subDir, lerr := fileLocation.NewLocation("other/")
+	s.NoError(lerr, "unexpected error creating a new location")
+
+	s.Equal("/foo/other/", subDir.Path())
+
+	relDir, lerr2 := subDir.NewLocation("../../bar/")
+	s.NoError(lerr2, "unexpected error creating a new location")
+	s.Equal("/bar/", relDir.Path(), "relative dot path works")
+}
+
+//TestNewLocationRelativePath tests to see whether a file can be made by passing a
+//relative path to a location object that technically does not exist
+func (s *memLocationTest) TestNewLocationRelativePath() {
+
+	newFile, nerr := s.fileSystem.NewFile("C", "/newLocTest/dir/file.txt")
+	s.NoError(nerr, "unexpected error creating a file")
+
+	_, werr := newFile.Write([]byte("Hello world!"))
+	s.NoError(werr, "unexpected write error")
+
+	s.NoError(newFile.Close(), "unexpected error closing file")
+
+	loc, nerr2 := s.fileSystem.NewLocation("C", "/newLocTest/")
+	s.NoError(nerr2, "unexpected error creating a new location")
+	s.True(loc.Exists())
+
+	otherFile, lerr := loc.NewFile("dir/file2.txt")
+	s.NoError(lerr, "unexpected error creating a file from location")
+
+	s.NoError(otherFile.Touch(), "unexpected error touching file")
+	s.Equal(newFile.Location().Path(), otherFile.Location().Path(), "absolute location paths should be equal")
+
+}
+
+//TestNewFile tests that location can create a file at its current path
+func (s *memLocationTest) TestNewFile() {
+	loc, err := s.fileSystem.NewLocation("", "/foo/bar/baz/")
+	s.NoError(err, "unexpected error creating a new location")
+
+	newfile, nerr := loc.NewFile("../../bam/this.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.Equal("/foo/bam/this.txt", newfile.Path(), "relative dot path works")
+}
+
+//TestNewFile creates two files with the same name and ensures
+//that the second creation returns a reference to the first
+func (s *memLocationTest) TestNewFileSameName() {
+	sharedPath := "/path/to/file.txt"
+	firstFile, err := s.fileSystem.NewFile("", sharedPath)
+
+	s.NoError(err, "unexpected error creating a file")
+	location := firstFile.Location()
+
+	expectedText := "hey y'all!"
+	_, err = firstFile.Write([]byte(expectedText))
+	s.NoError(err, "unexpected error writing to file")
+
+	secondFile, err := location.NewFile(path.Base(sharedPath))
+	s.NoError(err, "unexpected error creating a file")
+	expectedSlice := make([]byte, len(expectedText))
+
+	//since secondFile references firstFile, reading will throw an error as we never closed or seeked firstFile
+	_, err = secondFile.Read(expectedSlice)
+	s.Error(err, "expected read error since firstFile was never closed")
+
+	//after this call, we can expect to be able to read from secondFile since its reference, firstFile, was closed
+	s.NoError(firstFile.Close(), "unexpected error closing file")
+
+	_, err = secondFile.Read(expectedSlice)
+	s.NoError(err, "unexpected read error")
+
+	s.Equal(expectedText, string(expectedSlice))
+
+}
+
+//TestChangeDir tests that we can change the directory on a location but that it doesn't change the file's location
+func (s *memLocationTest) TestChangeDir() {
+
+	newFile, nerr := s.fileSystem.NewFile("", "/dir/to/change/change.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	loc := newFile.Location()
+
+	//changing directory
+	s.NoError(loc.ChangeDir("extraDir/"), "unexpected error while changing directory")
+	exists, eerr := loc.Exists()
+	s.NoError(eerr, "unexpected error checking for Existence")
+	s.True(exists)
+	s.False(newFile.Location().Path() == loc.Path())
+}
+
+//TestVolume makes sure that the mem-fs returns the empty string for its volume
+func (s *memLocationTest) TestVolume() {
+
+	newFile, nerr := s.fileSystem.NewFile("D:", "/path/to/file/example.txt")
+	s.NoError(nerr, "unexpected error creating a file")
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+	s.NoError(newFile.Close(), "unexpected error closing file")
+	// For Unix, this returns an empty string. For windows, it would be something like 'C:'
+	s.Equal("D:", newFile.Location().Volume())
+}
+
+//TestPath makes sure that locations return the correct paths, along with leading and trailing slashes
+func (s *memLocationTest) TestPath() {
+	file, nerr := s.fileSystem.NewFile("", "/some/file/test.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+
+	s.NoError(file.Touch(), "unexpected error touching file")
+	location := file.Location()
+	s.Equal("/some/file/", location.Path())
+	//deleting file
+	s.NoError(file.Delete(), "Delete failed unexpectedly")
+}
+
+//TestURI ensures that URI's for locations come out in the correct format
+func (s *memLocationTest) TestURI() {
+	file, nerr := s.fileSystem.NewFile("", "/some/file/test.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+	s.NoError(file.Touch(), "unexpected error touching file")
+	location := file.Location()
+	_, eerr := location.Exists()
+	s.NoError(eerr, "unexpected error checking for existence")
+	expected := "mem:///some/file/"
+	s.Equal(expected, location.URI(), "%s does not match %s", location.URI(), expected)
+	s.NoError(file.Delete(), "delete failed unexpectedly")
+}
+
+//TestStringer tests the implementation of io.Stringer
+func (s *memLocationTest) TestStringer() {
+	file, nerr := s.fileSystem.NewFile("C", "/some/file/test.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+
+	s.NoError(file.Touch(), "unexpected error touching file")
+	location := file.Location()
+	expected := "mem://C/some/file/"
+	s.Equal(expected, location.String(), "%s does not match %s", location.String(), expected)
+	//deleting file
+	s.NoError(file.Delete(), "delete failed unexpectedly")
+}
+
+//TestDeleteFile makes files, writes to them, deletes them, all while asserting things like existence and errors
+func (s *memLocationTest) TestDeleteFile() {
+
+	newFile, err := s.fileSystem.NewFile("", "/home/bar.txt")
+	s.NoError(err, "unexpected error creating a new file")
+	//attempt to delete newFile
+	s.Error(newFile.Delete(), "expected existence error") //expected an error since newFile does not yet exist
+	s.NoError(newFile.Touch(), "unexpected error touching file")
+
+	otherFile, nerr := s.fileSystem.NewFile("", "/foo.txt")
+	s.NoError(nerr, "unexpected error creating a new file")
+
+	//attempt to delete otherFile
+	s.Error(otherFile.Location().DeleteFile(otherFile.Name()), "expected existence error") //want to catch the delete error
+	s.NoError(otherFile.Touch(), "unexpected error touching file")                         //bring it to existence with a touch
+	existence, eerr := otherFile.Exists()
+	s.True(existence)
+	s.NoError(eerr, "unexpected existence error")
+
+	//deleting otherFile
+	s.NoError(otherFile.Location().DeleteFile(otherFile.Name()), "unexpected existence error")
+	existence1, eerr1 := otherFile.Exists()
+	s.False(existence1)
+	s.NoError(eerr1, "unexpected existence error")
+
+}
+
+func TestMemLocation(t *testing.T) {
+	suite.Run(t, new(memLocationTest))
+}
diff --git a/backend/mem/test_files/foo.txt b/backend/mem/test_files/foo.txt
new file mode 100644
index 0000000..c57eff5
--- /dev/null
+++ b/backend/mem/test_files/foo.txt
@@ -0,0 +1,1 @@
+Hello World!
\ No newline at end of file
diff --git a/backend/testsuite/backend_integration_test.go b/backend/testsuite/backend_integration_test.go
index 3ec5921..66d521e 100644
--- a/backend/testsuite/backend_integration_test.go
+++ b/backend/testsuite/backend_integration_test.go
@@ -15,6 +15,7 @@ import (
 
 	"github.com/c2fo/vfs/v5"
 	"github.com/c2fo/vfs/v5/backend/gs"
+	"github.com/c2fo/vfs/v5/backend/mem"
 	_os "github.com/c2fo/vfs/v5/backend/os"
 	"github.com/c2fo/vfs/v5/backend/s3"
 	"github.com/c2fo/vfs/v5/utils"
@@ -46,6 +47,11 @@ func copyOsLocation(loc vfs.Location) vfs.Location {
 	return ret
 }
 
+func copyMemLocation(loc vfs.Location) vfs.Location {
+	cp := *loc.(*mem.Location)
+	return &cp
+}
+
 func copyS3Location(loc vfs.Location) vfs.Location {
 	cp := *loc.(*s3.Location)
 	return &cp
@@ -69,6 +75,8 @@ func (s *vfsTestSuite) SetupSuite() {
 			s.testLocations[l.FileSystem().Scheme()] = copyS3Location(l)
 		case "gs":
 			s.testLocations[l.FileSystem().Scheme()] = copyGSLocation(l)
+		case "mem":
+			s.testLocations[l.FileSystem().Scheme()] = copyMemLocation(l)
 		default:
 			panic(fmt.Sprintf("unknown scheme: %s", l.FileSystem().Scheme()))
 		}
@@ -717,14 +725,15 @@ func (s *vfsTestSuite) File(baseLoc vfs.Location) {
 	//capture last modified
 	modified, err := touchedFile.LastModified()
 	s.NoError(err)
-
-	//wait for eventual constency
+	modifiedDeRef := *modified
+	//wait for eventual consistency
 	time.Sleep(1 * time.Second)
 	err = touchedFile.Touch()
 	s.NoError(err)
 	newModified, err := touchedFile.LastModified()
+
 	s.NoError(err)
-	s.True(newModified.UnixNano() > modified.UnixNano(), "touch updated modified date for %s", touchedFile)
+	s.True(newModified.UnixNano() > modifiedDeRef.UnixNano(), "touch updated modified date for %s", touchedFile)
 
 	/*
 		Delete unlinks the File on the file system.
@@ -736,6 +745,31 @@ func (s *vfsTestSuite) File(baseLoc vfs.Location) {
 	exists, err = srcFile.Exists()
 	s.NoError(err)
 	s.False(exists, "file no longer exists")
+
+	//The following blocks test that an error is thrown when these operations are called on a non-existent file
+	srcFile, err = srcLoc.NewFile("thisFileDoesNotExist")
+	s.NoError(err, "unexpected error creating file")
+
+	exists, err = srcFile.Exists()
+	s.NoError(err)
+	s.False(exists, "file should not exist")
+
+	size, err = srcFile.Size()
+	s.Error(err, "expected error because file does not exist")
+	s.Equal(uint64(0x0), size)
+
+	_, err = srcFile.LastModified()
+	s.Error(err, "expected error because file does not exist")
+
+	seeked, err := srcFile.Seek(1, 2)
+	s.Error(err, "expected error because file does not exist")
+	s.Equal(int64(0x0), seeked)
+
+	_, err = srcFile.Read(make([]byte, 1))
+	s.Error(err, "expected error because file does not exist")
+
+	//end existence tests
+
 }
 
 func TestVFS(t *testing.T) {
diff --git a/utils/utils_test.go b/utils/utils_test.go
index eee7c21..566f39b 100644
--- a/utils/utils_test.go
+++ b/utils/utils_test.go
@@ -324,7 +324,7 @@ func (s *utilsTest) TestValidateRelFilePath() {
 		},
 		{
 			path:         "",
-			passExpected: true,
+			passExpected: false,
 			message:      "empty string",
 		},
 		{