From 8f2b0a36efb6435d23aa728e912732aa3e421fee Mon Sep 17 00:00:00 2001 From: Andrey Kiselev Date: Sat, 15 Aug 2020 13:08:57 +0300 Subject: [PATCH 1/2] HW8 is completed --- hw08_envdir_tool/env_reader.go | 76 +++++++++++++++++++++++++++-- hw08_envdir_tool/env_reader_test.go | 44 ++++++++++++++++- hw08_envdir_tool/executor.go | 36 ++++++++++++-- hw08_envdir_tool/executor_test.go | 28 ++++++++++- hw08_envdir_tool/go.mod | 4 +- hw08_envdir_tool/main.go | 13 ++++- 6 files changed, 188 insertions(+), 13 deletions(-) diff --git a/hw08_envdir_tool/env_reader.go b/hw08_envdir_tool/env_reader.go index 1a1f12d..a3c9a75 100644 --- a/hw08_envdir_tool/env_reader.go +++ b/hw08_envdir_tool/env_reader.go @@ -1,10 +1,78 @@ package main +import ( + "errors" + "io/ioutil" + "os" + "path/filepath" + "strings" + "unicode" +) + type Environment map[string]string -// ReadDir reads a specified directory and returns map of env variables. -// Variables represented as files where filename is name of variable, file first line is a value. func ReadDir(dir string) (Environment, error) { - // Place your code here - return nil, nil + // ReadDir reads a specified directory and returns map of env variables. + // Variables represented as files where filename is name of variable, file first line is a value. + env := make(Environment) + dataDir, err := ioutil.ReadDir(dir) + if err != nil { + return nil, err + } + + for _, d := range dataDir { + fileName := d.Name() + + // skip recursive directory search and files with "=" + if d.IsDir() || strings.Contains(fileName, "=") { + continue + } + + filePath := filepath.Join(dir, fileName) + val, err := readFileValue(filePath) + // skip file with error + if err != nil { + continue + } + env[fileName] = val + } + + return env, nil +} + +func readFileValue(filePath string) (string, error) { + // function read file and return first "trail trimmed" string + r, err := os.Open(filePath) + if err != nil { + return "", err + } + defer r.Close() + + st, err := r.Stat() + if err != nil { + return "", err + } + + // don't read empty files + if st.Size() <= 0 { + return "", nil + } + + bytes, err := ioutil.ReadAll(r) + if err != nil { + return "", err + } + + // read only first line if it doesn't not empty + for _, val := range strings.Split(string(bytes), "\n") { + if val == "" { + continue + } + + // convering 0x00 to \n magic + val = strings.ReplaceAll(val, string('\x00'), "\n") + return strings.TrimRightFunc(val, unicode.IsSpace), nil + } + + return "", errors.New("empty result") } diff --git a/hw08_envdir_tool/env_reader_test.go b/hw08_envdir_tool/env_reader_test.go index 7962c06..6743dcf 100644 --- a/hw08_envdir_tool/env_reader_test.go +++ b/hw08_envdir_tool/env_reader_test.go @@ -1,7 +1,47 @@ package main -import "testing" +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestReadFileValue(t *testing.T) { + t.Run("not exist file", func(t *testing.T) { + _, err := readFileValue("testdata/env/not-exist") + require.True(t, os.IsNotExist(err)) + }) + + t.Run("empty file", func(t *testing.T) { + val, err := readFileValue("testdata/env/UNSET") + require.Nil(t, err) + require.Equal(t, "", val) + }) + + t.Run("heavy variable", func(t *testing.T) { + sample := fmt.Sprintf(" foo\nwith new line") + + val, err := readFileValue("testdata/env/FOO") + require.Nil(t, err) + require.Equal(t, sample, val) + }) +} func TestReadDir(t *testing.T) { - // Place your code here + t.Run("not exist dir", func(t *testing.T) { + _, err := ReadDir("testdata/env-not-exist") + err, ok := err.(*os.PathError) + + require.True(t, ok) + require.NotNil(t, err) + }) + + t.Run("complex parse check", func(t *testing.T) { + envMap, err := ReadDir("testdata/env") + require.Nil(t, err) + + require.Equal(t, envMap, Environment{"UNSET": "", "BAR": "bar", "HELLO": "\"hello\"", "FOO": " foo\nwith new line"}) + }) } diff --git a/hw08_envdir_tool/executor.go b/hw08_envdir_tool/executor.go index f01d6be..8239090 100644 --- a/hw08_envdir_tool/executor.go +++ b/hw08_envdir_tool/executor.go @@ -1,7 +1,37 @@ package main -// RunCmd runs a command + arguments (cmd) with environment variables from env +import ( + "fmt" + "os/exec" +) + func RunCmd(cmd []string, env Environment) (returnCode int) { - // Place your code here - return + // RunCmd runs a command + arguments (cmd) with environment variables from env + + execEnv := make([]string, len(env)) + for envVariable, value := range env { + // skip empty values + if value == "" { + continue + } + execEnv = append(execEnv, fmt.Sprintf("%s=%s", envVariable, value)) + } + + command, args := cmd[0], cmd[1:] + cmdExec := exec.Command(command, args...) + cmdExec.Env = execEnv + + stdoutStderr, err := cmdExec.CombinedOutput() + if err != nil { + exitError, ok := err.(*exec.ExitError) + if ok { + return exitError.ExitCode() + } + + // os.PathError and other non caugth errors + return 1 + } + + fmt.Printf("%s", stdoutStderr) + return 0 } diff --git a/hw08_envdir_tool/executor_test.go b/hw08_envdir_tool/executor_test.go index 6402ce3..b081a5a 100644 --- a/hw08_envdir_tool/executor_test.go +++ b/hw08_envdir_tool/executor_test.go @@ -1,7 +1,31 @@ package main -import "testing" +import ( + "io/ioutil" + "os" + "testing" + + "github.com/stretchr/testify/require" +) func TestRunCmd(t *testing.T) { - // Place your code here + t.Run("not exist file", func(t *testing.T) { + exitCode := RunCmd([]string{"not-exist-command"}, nil) + require.EqualValues(t, 1, exitCode) + }) + + t.Run("check output", func(t *testing.T) { + r, w, err := os.Pipe() + require.Nil(t, err) + + os.Stdout = w + exitCode := RunCmd([]string{"echo", "-n", "test"}, nil) + w.Close() + + require.EqualValues(t, 0, exitCode) + out, err := ioutil.ReadAll(r) + require.Nil(t, err) + + require.Equal(t, "test", string(out)) + }) } diff --git a/hw08_envdir_tool/go.mod b/hw08_envdir_tool/go.mod index 8536af6..08248e8 100644 --- a/hw08_envdir_tool/go.mod +++ b/hw08_envdir_tool/go.mod @@ -1,3 +1,5 @@ -module github.com/fixme_my_friend/hw08_envdir_tool +module github.com/ezhk/golang-learning/hw08_envdir_tool go 1.14 + +require github.com/stretchr/testify v1.6.1 diff --git a/hw08_envdir_tool/main.go b/hw08_envdir_tool/main.go index 441440a..bc2d51a 100644 --- a/hw08_envdir_tool/main.go +++ b/hw08_envdir_tool/main.go @@ -1,5 +1,16 @@ package main +import ( + "os" +) + func main() { - // Place your code here + envDir, cmd := os.Args[1], os.Args[2:] + environment, err := ReadDir(envDir) + if err != nil { + os.Exit(1) + } + + exitCode := RunCmd(cmd, environment) + os.Exit(exitCode) } From cb58b3124f2f8f3b2ef4cede56401601dd0d029c Mon Sep 17 00:00:00 2001 From: Andrey Kiselev Date: Sat, 15 Aug 2020 13:57:32 +0300 Subject: [PATCH 2/2] linting applied --- hw08_envdir_tool/env_reader.go | 1 + hw08_envdir_tool/executor.go | 1 + 2 files changed, 2 insertions(+) diff --git a/hw08_envdir_tool/env_reader.go b/hw08_envdir_tool/env_reader.go index a3c9a75..deff79a 100644 --- a/hw08_envdir_tool/env_reader.go +++ b/hw08_envdir_tool/env_reader.go @@ -71,6 +71,7 @@ func readFileValue(filePath string) (string, error) { // convering 0x00 to \n magic val = strings.ReplaceAll(val, string('\x00'), "\n") + return strings.TrimRightFunc(val, unicode.IsSpace), nil } diff --git a/hw08_envdir_tool/executor.go b/hw08_envdir_tool/executor.go index 8239090..461979b 100644 --- a/hw08_envdir_tool/executor.go +++ b/hw08_envdir_tool/executor.go @@ -33,5 +33,6 @@ func RunCmd(cmd []string, env Environment) (returnCode int) { } fmt.Printf("%s", stdoutStderr) + return 0 }