diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 705ced26ba490e54fd2f77a26cf7fae1a7ddd1f5..da26fe7a0da14e705c49f6f39443403fbbd58710 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,7 @@ variables: REPO_DIR: gitlab.com/elixxir REPO_NAME: primitives DOCKER_IMAGE: elixxirlabs/cuda-go:latest - MIN_CODE_COVERAGE: "89.0" + MIN_CODE_COVERAGE: "80.0" before_script: - go version || echo "Go executable not found." diff --git a/go.mod b/go.mod index bfb587709d7358391f38ec6e7224427c47239781..d4484a47709a9fb99cd6d28837f51da57a0ca46c 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module gitlab.com/xx_network/primitives require ( + github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 github.com/spf13/jwalterweatherman v1.1.0 - gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d ) go 1.13 diff --git a/go.sum b/go.sum index e26a016487848e53b5fe8f855816ddd44318b0ed..e3efee23807754c3ad20d16996302db153d8d58e 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,26 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -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/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d h1:OKWTmYN5q8XVHo8JXThIH0TCuvl/fLXR7MGVacpqfRg= -gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= +gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/id/idf/idf.go b/id/idf/idf.go index 7770ee6d941b8a8a0ee9cb1f68c28c1c85d8def4..8878b03fe2687d2f7a8c14457ccbba93ab78947f 100644 --- a/id/idf/idf.go +++ b/id/idf/idf.go @@ -14,7 +14,7 @@ import ( "encoding/json" "github.com/pkg/errors" "gitlab.com/xx_network/primitives/id" - "gitlab.com/elixxir/primitives/utils" + "gitlab.com/xx_network/primitives/utils" ) // The length of the salt byte array diff --git a/id/idf/idf_test.go b/id/idf/idf_test.go index 9cdc7f845f3387dad4ba07084244943b53c9a15a..7ba263801f18bf073c239307c5aa1484faf4881c 100644 --- a/id/idf/idf_test.go +++ b/id/idf/idf_test.go @@ -9,7 +9,7 @@ package idf import ( "bytes" "gitlab.com/xx_network/primitives/id" - "gitlab.com/elixxir/primitives/utils" + "gitlab.com/xx_network/primitives/utils" "os" "reflect" "testing" diff --git a/utils/gen.go b/utils/gen.go new file mode 100644 index 0000000000000000000000000000000000000000..594ac279cfb7d4605d9e58c02c295428085ddacc --- /dev/null +++ b/utils/gen.go @@ -0,0 +1,83 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the LICENSE file // +//////////////////////////////////////////////////////////////////////////////////////////// + +// Provides functions for writing a version information file + +package utils + +import ( + "bufio" + "io/ioutil" + "os" + "os/exec" + "strings" + "text/template" + "time" +) + +// Version file generation consumed by higher-level repos +func GenerateVersionFile(version string) { + gitversion := GenerateGitVersion() + deps := ReadGoMod() + + f, err := os.Create("version_vars.go") + if err != nil { + panic(err) + } + + err = packageTemplate.Execute(f, struct { + Timestamp time.Time + GITVER string + DEPENDENCIES string + VERSION string + }{ + Timestamp: time.Now(), + GITVER: gitversion, + DEPENDENCIES: deps, + VERSION: version, + }) + if err != nil { + panic(err) + } + + err = f.Close() + if err != nil { + panic(err) + } +} + +// Determine current Git version information +func GenerateGitVersion() string { + cmd := exec.Command("git", "show", "--oneline") + stdoutStderr, err := cmd.CombinedOutput() + if err != nil { + panic(err) + } + scanner := bufio.NewScanner(strings.NewReader(string(stdoutStderr))) + for scanner.Scan() { + return scanner.Text() + } + return "UNKNOWNVERSION" +} + +// Read in go modules file +func ReadGoMod() string { + r, err := ioutil.ReadFile("go.mod") + if err != nil { + panic(err) + } + return string(r) +} + +// Template for version_vars.go +var packageTemplate = template.Must(template.New("").Parse( + "// Code generated by go generate; DO NOT EDIT.\n" + + "// This file was generated by robots at\n" + + "// {{ .Timestamp }}\n" + + "package cmd\n\n" + + "const GITVERSION = `{{ .GITVER }}`\n" + + "const SEMVER = \"{{ .VERSION }}\"\n" + + "const DEPENDENCIES = `{{ .DEPENDENCIES }}`\n")) diff --git a/utils/utils.go b/utils/utils.go new file mode 100644 index 0000000000000000000000000000000000000000..92fbb73d4d7f05c622cc4e4c660d11a190f258dc --- /dev/null +++ b/utils/utils.go @@ -0,0 +1,215 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the LICENSE file // +//////////////////////////////////////////////////////////////////////////////////////////// + +// Package utils contains general utility functions used by our system. +// They are generic and perform basic tasks. As of writing, it mostly contains +// file IO functions to make our system be able file IO independent of platform. + +package utils + +import ( + "github.com/mitchellh/go-homedir" + "github.com/pkg/errors" + "io/ioutil" + "os" + "path/filepath" +) + +const ( + // Permissions for new files/directories + FilePerms = os.FileMode(0644) + DirPerms = os.ModePerm +) + +// ExpandPath replaces the '~' character with the user's home directory and +// cleans the path using the following rules: +// 1. Replace multiple Separator elements with a single one. +// 2. Eliminate each . path name element (the current directory). +// 3. Eliminate each inner .. path name element (the parent directory) +// along with the non-.. element that precedes it. +// 4. Eliminate .. elements that begin a rooted path: that is, replace +// "/.." by "/" at the beginning of a path, assuming Separator is '/'. +// 5. The returned path ends in a slash only if it represents a root +// directory. +// 6. Any occurrences of slash are replaced by Separator. +func ExpandPath(path string) (string, error) { + // If the path is empty, then return nothing + if path == "" { + return "", nil + } + + // Replace the '~' character with the user's home directory + path, err := homedir.Expand(path) + if err != nil { + return "", err + } + + // Cleans the path using the rules in the function description + path = filepath.Clean(path) + + return path, nil +} + +// mkdirAll creates all the folders in a path that do not exist. If the path +// already exists, then nothing is done and nil is returned. +func mkdirAll(path string, perm os.FileMode) error { + // Strip file name from the path + dir := filepath.Dir(path) + + // Create the directories + return os.MkdirAll(dir, perm) +} + +// MakeDirs expands and cleans the path and then creates all the folders in a +// path that do not exist. +func MakeDirs(path string, perm os.FileMode) error { + // Expand '~' to user's home directory and clean the path + path, err := ExpandPath(path) + if err != nil { + return err + } + + // Create all directories in path, if they do not already exist + return mkdirAll(path, perm) +} + +// WriteFile creates any directories in the path that do not exists and write +// the specified data to the file. +func WriteFile(path string, data []byte, filePerm, dirPerm os.FileMode) error { + // Expand '~' to user's home directory and clean the path + path, err := ExpandPath(path) + if err != nil { + return err + } + + // Make an directories in the path that do not already exist + err = mkdirAll(path, dirPerm) + if err != nil { + return err + } + + // Write to the specified file + err = ioutil.WriteFile(path, data, filePerm) + return err +} + +// ReadFile expands and cleans the specified path, reads the file, and returns +// its contents. +func ReadFile(path string) ([]byte, error) { + // Expand '~' to user's home directory and clean the path + path, err := ExpandPath(path) + if err != nil { + return nil, err + } + + // Read the file and return the contents + return ioutil.ReadFile(path) +} + +// Exist checks if a file or directory exists at the specified path. +func Exists(path string) bool { + // Check if a file or directory exists at the path + _, exists := exists(path) + + return exists +} + +// FileExists checks if the file at the path exists. It returns false if the +// file does not exist or if it is a directory. +func FileExists(path string) bool { + // Get file description information and if the file exists + info, exists := exists(path) + + isFile := false + if info != nil { + isFile = !info.IsDir() + } + + // Check if the file is a directory + return exists && isFile +} + +// DirExists checks if the directory at the path exists. It returns false if the +// directory does not exist or if it is a file. +func DirExists(path string) bool { + // Get file description information and if the directory exists + info, exists := exists(path) + + // Check if the file is a directory + return exists && info.IsDir() +} + +// exist checks if a file or directory exists at the specified path and also +// returns the file's FileInfo. +func exists(path string) (os.FileInfo, bool) { + // Expand '~' to user's home directory and clean the path + path, err := ExpandPath(path) + if err != nil { + return nil, false + } + + // Get file description information and path errors + info, err := os.Stat(path) + + // Check if a file or directory exists at the path + return info, !os.IsNotExist(err) +} + +// SearchDefaultLocations searches for a file path in a default directory in +// a number of hard-coded paths, including the user's home folder and /etc/. If +// the file is found, its full path is returned. Otherwise, the path is blank +// and an error is returned. +// +// Note that defaultDirectory MUST be a relative path. By default, when checking +// the home directory a "." is prepended the to defaultDirectory. +func SearchDefaultLocations(defaultFileName string, defaultDirectory string) (string, error) { + // Get the user's home directory + defaultDirs, err := getDefaultSearchDirs(defaultDirectory) + if err != nil { + return "", errors.Errorf("Could not get home directory: %+v", err) + } + + // Search the directories for the file + for _, dir := range defaultDirs { + // Format the path and check for errors + path := dir + "/" + defaultFileName + foundFilePath, err := ExpandPath(path) + if err != nil { + return "", errors.Errorf("Error expanding path %s: %v", path, err) + } + + // If the file exists, return its path + if FileExists(foundFilePath) { + return foundFilePath, nil + } + } + + return "", errors.Errorf("Could not find %s in any of the directories: %v", + defaultFileName, defaultDirs) +} + +// getDefaultSearchDirs retrieves the list of default directories to search for +// configuration files in. Note that defaultDirectory MUST be a relative path. +func getDefaultSearchDirs(defaultDirectory string) ([]string, error) { + var searchDirs []string + + // Get the user's home directory + home, err := homedir.Dir() + if err != nil { + return nil, errors.Errorf("Could not get home directory: %+v", err) + } + + // Add the home directory to the search + searchDirs = append(searchDirs, filepath.Clean(home+"/."+defaultDirectory+"/")) + + // Add /opt/ to the search + searchDirs = append(searchDirs, filepath.Clean("/opt/"+defaultDirectory+"/")) + + // Add /etc/ to the search + searchDirs = append(searchDirs, filepath.Clean("/etc/"+defaultDirectory+"/")) + + return searchDirs, nil +} diff --git a/utils/utils_test.go b/utils/utils_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d2fbc105ac2a688a81f7c3b74436a33a28688cb3 --- /dev/null +++ b/utils/utils_test.go @@ -0,0 +1,622 @@ +//////////////////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the LICENSE file // +//////////////////////////////////////////////////////////////////////////////////////////// + +// Contains utility operations used throughout the repo + +package utils + +import ( + "bytes" + "errors" + "fmt" + "github.com/mitchellh/go-homedir" + "os" + "path/filepath" + "reflect" + "testing" + "time" +) + +const sep = string(filepath.Separator) + +// Tests that ExpandPath() properly expands the the "~" character. +func TestExpandPath_Happy(t *testing.T) { + path := sep + "test123" + sep + "test.txt" + testPath := "~" + path + homeDir, _ := homedir.Dir() + expectPath := homeDir + path + newPath, err := ExpandPath(testPath) + + if err != nil { + t.Errorf("ExpandPath() produced an unexpected error: %v", err) + } + + if newPath != expectPath { + t.Errorf("ExpandPath() did not correctly expand the \"~\" character in the path %s"+ + "\n\texpected: %s\n\treceived: %s", testPath, expectPath, newPath) + } +} + +// Tests that the path is unchanged by ExpandPath(). +func TestExpandPath_Default(t *testing.T) { + path := sep + "test123" + sep + "test.txt" + newPath, err := ExpandPath(path) + + if err != nil { + t.Errorf("ExpandPath() produced an unexpected error: %v", err) + } + + if newPath != path { + t.Errorf("ExpandPath() unexpectedly modified the path %s"+ + "\n\texpected: %s\n\treceived: %s", path, path, newPath) + } +} + +// Tests that for an empty path, ExpandPath() returns an empty string. +func TestExpandPath_EmptyPath(t *testing.T) { + path := "" + newPath, err := ExpandPath(path) + + if err != nil { + t.Errorf("ExpandPath() produced an unexpected error: %v", err) + } + + if newPath != path { + t.Errorf("ExpandPath() unexpectedly modified the path %s"+ + "\n\texpected: %s\n\treceived: %s", path, path, newPath) + } +} + +// Tests that ExpandPath() returns an error for an invalid path. +func TestExpandPath_PathError(t *testing.T) { + path := "~a/test/test.txt" + _, err := ExpandPath(path) + + if err == nil { + t.Errorf("ExpandPath() did not produce error when expected:"+ + "\n\texpected: %v\n\treceived: %v", + errors.New("cannot expand user-specific home dir"), err) + } +} + +// Tests that mkdirAll() creates the directories in the specified path (includes +// the file name) by checking if the directory structure exists. +func TestMkdirAll(t *testing.T) { + path := "temp/temp2/test.txt" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := mkdirAll(path, DirPerms) + if err != nil { + t.Errorf("mkdirAll() produced an unexpected error: %v", err) + } + + if _, err = os.Stat(filepath.Dir(path)); os.IsExist(err) { + t.Errorf("mkdirAll() did not correctly make the directories:"+ + "\n\t%s", path) + } +} + +// Tests that mkdirAll() creates the directories in the specified path (does not +// include the file name) by checking if the directory structure exists. +func TestMkdirAll_DirectoryPath(t *testing.T) { + path := "temp/temp2/" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := mkdirAll(path, DirPerms) + if err != nil { + t.Errorf("mkdirAll() produced an unexpected error: %v", err) + } + + if _, err = os.Stat(filepath.Dir(path)); os.IsExist(err) { + t.Errorf("mkdirAll() did not correctly make the directories:"+ + "\n\t%s", path) + } +} + +// Tests that mkdirAll() does nothing for an empty path. +func TestMkdirAll_EmptyPath(t *testing.T) { + path := "" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := mkdirAll(path, DirPerms) + if err != nil { + t.Errorf("mkdirAll() produced an unexpected error: %v", err) + } + + if _, err = os.Stat(filepath.Dir(path)); os.IsExist(err) { + t.Errorf("mkdirAll() did not correctly make the directories:"+ + "\n\t%s", path) + } +} + +// Tests MakeDirs() by checking if the directory structure exists. +func TestMakeDirs(t *testing.T) { + path := "temp/temp2/test.txt" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := MakeDirs(path, DirPerms) + if err != nil { + t.Errorf("MakeDirs() produced an unexpected error: %v", err) + } + + if _, err = os.Stat(filepath.Dir(path)); os.IsExist(err) { + t.Errorf("MakeDirs() did not correctly make the directories:"+ + "\n\t%s", path) + } +} + +// Tests that MakeDirs() produces an error on an invalid path. +func TestMakeDirs_PathError(t *testing.T) { + path := "~a/test/test.txt" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := MakeDirs(path, DirPerms) + if err == nil { + t.Errorf("MakeDirs() did not produce error when expected:"+ + "\n\texpected: %v\n\treceived: %v", + errors.New("cannot expand user-specific home dir"), err) + } +} + +// Tests WriteFile() by checking if the directory structure and the +// file exists. +func TestWriteFile(t *testing.T) { + dirPath := "temp/temp2" + path := dirPath + "/test.txt" + data := []byte("test data") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll("temp/") + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", "temp/", err) + } + }() + + err := os.MkdirAll(dirPath, os.ModePerm) + if err != nil { + t.Errorf("MkdirAll() produced an unexpected error: %v", err) + } + + err = WriteFile(path, data, DirPerms, FilePerms) + if err != nil { + t.Errorf("WriteFile() produced an unexpected error: %v", err) + } + + if _, err = os.Stat(path); os.IsExist(err) { + t.Errorf("WriteFile() did not correctly make the directories:"+ + "\n\t%s", path) + } +} + +// Tests that WriteFile() returns an error with a malformed path. +func TestWriteFile_PathError(t *testing.T) { + path := "~a/temp/temp2/test.txt" + data := []byte("test data") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := WriteFile(path, data, DirPerms, FilePerms) + if err == nil { + t.Errorf("WriteFile() did not produce error when expected:"+ + "\n\texpected: %v\n\treceived: %v", + errors.New("cannot expand user-specific home dir"), err) + } +} + +// Tests that ReadFile() properly reads the contents of a file created by +// WriteFile(). +func TestReadFile(t *testing.T) { + path := "test.txt" + data := []byte("Test string.") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := WriteFile(path, data, FilePerms, FilePerms) + if err != nil { + t.Errorf("WriteFile() produced an unexpected error: %v", err) + } + + testData, err := ReadFile(path) + if err != nil { + t.Errorf("ReadFile() produced an unexpected error: %v", err) + } + + if !bytes.Equal(testData, data) { + t.Errorf("ReadFile() did not return the correct data from the file %s"+ + "\n\texpected: %s\n\treceived: %s", path, data, testData) + } +} + +// Tests that ReadFile() returns an error with a malformed path. +func TestReadFile_PathError(t *testing.T) { + path := "~a/temp/temp2/test.txt" + _, err := ReadFile(path) + + if err == nil { + t.Errorf("ReadFile() did not produce error when expected:"+ + "\n\texpected: %v\n\treceived: %v", + errors.New("cannot expand user-specific home dir"), err) + } +} + +// Tests that TestExist() correctly finds a file that exists. +func TestExist(t *testing.T) { + path := "test.txt" + data := []byte("Test string.") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := WriteFile(path, data, FilePerms, FilePerms) + if err != nil { + t.Errorf("WriteFile() produced an unexpected error: %v", err) + } + + exists := Exists(path) + if !exists { + t.Errorf("Exists() did not find a file that should exist") + } +} + +// Tests that TestExist() correctly finds a directory that exists. +func TestExist_Dir(t *testing.T) { + path := "a/" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := MakeDirs(path+"d", DirPerms) + if err != nil { + t.Errorf("MakeDirs() produced an unexpected error: %v", err) + } + + exists := Exists(path) + if !exists { + t.Errorf("Exists() did not find a directory that should exist") + } +} + +// Tests that TestExist() returns false when a file does not exist. +func TestExist_NoFileError(t *testing.T) { + path := "test.txt" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + exists := Exists(path) + if exists { + t.Errorf("Exists() found a file when one does not exist") + } +} + +// Tests that FileExists() correctly finds a file that exists. +func TestFileExists(t *testing.T) { + path := "test.txt" + data := []byte("Test string.") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := WriteFile(path, data, FilePerms, FilePerms) + if err != nil { + t.Errorf("WriteFile() produced an unexpected error: %v", err) + } + + exists := FileExists(path) + if !exists { + t.Errorf("FileExists() did not find a file that should exist") + } +} + +// Tests that FileExists() false when the file is a directory. +func TestFileExists_DirError(t *testing.T) { + path := "a/d" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := MakeDirs(path, DirPerms) + if err != nil { + t.Errorf("MakeDirs() produced an unexpected error: %v", err) + } + + exists := FileExists(path) + if exists { + t.Errorf("FileExists() found a directory when it was looking for a file") + } +} + +// Tests that FileExists() returns false when a file does not exist. +func TestFileExists_NoFileError(t *testing.T) { + path := "test.txt" + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + exists := FileExists(path) + if exists { + t.Errorf("FileExists() found a file when one does not exist") + } +} + +// Tests that DirExists() correctly finds a directory that exists. +func TestDirExists(t *testing.T) { + path := "a/" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := MakeDirs(path+"d", DirPerms) + if err != nil { + t.Errorf("MakeDirs() produced an unexpected error: %v", err) + } + + exists := DirExists(path) + if !exists { + t.Errorf("DirExists() did not find a directory that should exist") + } +} + +// Tests that DirExists() false when the file is a directory. +func TestDirExists_FileError(t *testing.T) { + path := "test.txt" + data := []byte("Test string.") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := WriteFile(path, data, FilePerms, FilePerms) + if err != nil { + t.Errorf("WriteFile() produced an unexpected error: %v", err) + } + + exists := DirExists(path) + if exists { + t.Errorf("DirExists() found a file when it was looking for a directory") + } +} + +// Tests that DirExists() returns false when a file does not exist. +func TestDirExists_NoDirError(t *testing.T) { + path := "a/b/c/" + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + exists := FileExists(path) + if exists { + t.Errorf("DirExists() found a directroy when one does not exist") + } +} + +// Tests that Test_exist() correctly finds a file that exists and returns the +// correct FileInfo. +func Test_exist(t *testing.T) { + path := "test.txt" + data := []byte("Test string.") + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(path) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", path, err) + } + }() + + err := WriteFile(path, data, FilePerms, FilePerms) + if err != nil { + t.Errorf("WriteFile() produced an unexpected error: %v", err) + } + + info, exists := exists(path) + expectedInfo, err := os.Stat(path) + + if !exists && err != nil { + t.Errorf("exists() did not find a file that should exist:"+ + "\n\t%v", err) + } else if !exists { + t.Errorf("exists() did not find a file that should exist") + } + + if !reflect.DeepEqual(info, expectedInfo) { + t.Errorf("exists() did not return the expected FileInfo."+ + "\n\texpected: %v\n\treceived: %v", expectedInfo, info) + } +} + +// Tests that Test_exist() returns false when a file does not exist. and returns +// a nil FileInfo. +func Test_exist_NoFileError(t *testing.T) { + path := "test.txt" + + info, exists := exists(path) + + if exists { + t.Errorf("exists() found a file when one does not exist") + } + + if info != nil { + t.Errorf("exists() unexpectedly returned a non-nil FileInfo."+ + "\n\texpected: %v\n\treceived: %v", nil, info) + } +} + +// Tests that SearchDefaultLocations() finds the specified file in the user's +// home directory +func TestSearchDefaultLocations(t *testing.T) { + testDir := fmt.Sprintf("testDir-%d/", time.Now().Nanosecond()) + testFile := fmt.Sprintf("testFile-%d.txt", time.Now().Nanosecond()) + testPath := testDir + testFile + expectedPath, err := ExpandPath("~/." + testPath) + if err != nil { + t.Fatalf("ExpandPath() failed to exapnd the path %s: %+v", testPath, err) + } + expectedDir, err := ExpandPath("~/" + testDir) + if err != nil { + t.Fatalf("ExpandPath() failed to exapnd the path %s: %+v", testPath, err) + } + + // Delete the test file at the end + defer func() { + err := os.RemoveAll(expectedDir) + if err != nil { + t.Fatalf("Error deleting test file %#v:\n%v", expectedDir, err) + } + }() + + err = WriteFile(expectedPath, []byte("TEST"), FilePerms, DirPerms) + if err != nil { + t.Fatalf("WriteFile() failed to create file %s: %+v", testPath, err) + } + + foundPath, err := SearchDefaultLocations(testFile, testDir) + if err != nil { + t.Errorf("SearchDefaultLocations() produced an unexpected error: %+v", + err) + } + + if foundPath != expectedPath { + t.Errorf("SearchDefaultLocations() did not find the correct file."+ + "\n\texpected: %s\n\treceived: %s", expectedPath, foundPath) + } +} + +// Tests that SearchDefaultLocations() return an error when the file does not +// exist. +func TestSearchDefaultLocations_NotFoundError(t *testing.T) { + testDir := fmt.Sprintf(".testDir-%d/", time.Now().Nanosecond()) + testFile := fmt.Sprintf("testFile-%d.txt", time.Now().Nanosecond()) + + foundPath, err := SearchDefaultLocations(testFile, testDir) + if err == nil { + t.Errorf("SearchDefaultLocations() did not error when expected.") + } + + if foundPath != "" { + t.Errorf("SearchDefaultLocations() did not return an empty path on error."+ + "\n\texpected: %s\n\treceived: %s", "", foundPath) + } +} + +// Tests that getDefaultSearchDirs generates the correct list of default paths. +func TestGetDefaultSearchDirs(t *testing.T) { + testDir := "xxnetwork" + expectedDir0, err := ExpandPath("~/." + testDir + "/") + expectedDir1, err := ExpandPath("/opt/" + testDir + "/") + expectedDir2, err := ExpandPath("/etc/" + testDir + "/") + + testDirs, err := getDefaultSearchDirs(testDir) + if err != nil { + t.Errorf("getDefaultSearchDirs() produced an unxpected error: %+v", err) + } + + if testDirs[0] != expectedDir0 { + t.Errorf("getDefaultSearchDirs() did not return the correct path for "+ + "home.\n\texpected: %s\n\treceived: %s", expectedDir0, testDirs[0]) + } + + if testDirs[1] != expectedDir1 { + t.Errorf("getDefaultSearchDirs() did not return the correct path for "+ + "/etc/.\n\texpected: %s\n\treceived: %s", expectedDir1, testDirs[1]) + } + + if testDirs[2] != expectedDir2 { + t.Errorf("getDefaultSearchDirs() did not return the correct path for "+ + "/etc/.\n\texpected: %s\n\treceived: %s", expectedDir2, testDirs[2]) + } +}