From 4544dabd56483382dd973457e746adc872694481 Mon Sep 17 00:00:00 2001 From: makpoc Date: Tue, 13 Oct 2015 14:39:18 +0300 Subject: [PATCH 1/4] Add tests for command splitting --- middleware/commands_test.go | 138 ++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 middleware/commands_test.go diff --git a/middleware/commands_test.go b/middleware/commands_test.go new file mode 100644 index 00000000..3a5b3334 --- /dev/null +++ b/middleware/commands_test.go @@ -0,0 +1,138 @@ +package middleware + +import ( + "fmt" + "strings" + "testing" +) + +func TestSplitCommandAndArgs(t *testing.T) { + var parseErrorContent = "error parsing command:" + var noCommandErrContent = "no command contained in" + + tests := []struct { + input string + expectedCommand string + expectedArgs []string + expectedErrContent string + }{ + // Test case 0 - emtpy command + { + input: ``, + expectedCommand: ``, + expectedArgs: nil, + expectedErrContent: noCommandErrContent, + }, + // Test case 1 - command without arguments + { + input: `command`, + expectedCommand: `command`, + expectedArgs: nil, + expectedErrContent: ``, + }, + // Test case 2 - command with single argument + { + input: `command arg1`, + expectedCommand: `command`, + expectedArgs: []string{`arg1`}, + expectedErrContent: ``, + }, + // Test case 3 - command with multiple arguments + { + input: `command arg1 arg2`, + expectedCommand: `command`, + expectedArgs: []string{`arg1`, `arg2`}, + expectedErrContent: ``, + }, + // Test case 4 - command with single argument with space character - in quotes + { + input: `command "arg1 arg1"`, + expectedCommand: `command`, + expectedArgs: []string{`arg1 arg1`}, + expectedErrContent: ``, + }, + // Test case 4 - command with single argument with space character - escaped + { + input: `command arg1\ arg1`, + expectedCommand: `command`, + expectedArgs: []string{`arg1 arg1`}, + expectedErrContent: ``, + }, + // Test case 6 - command with escaped quote character + { + input: `command "arg1 \" arg1"`, + expectedCommand: `command`, + expectedArgs: []string{`arg1 " arg1`}, + expectedErrContent: ``, + }, + // Test case 7 - command with escaped backslash + { + input: `command '\arg1'`, + expectedCommand: `command`, + expectedArgs: []string{`\arg1`}, + expectedErrContent: ``, + }, + // Test case 8 - command with comments + { + input: `command arg1 #comment1 comment2`, + expectedCommand: `command`, + expectedArgs: []string{`arg1`}, + expectedErrContent: "", + }, + // Test case 9 - command with multiple spaces and tab character + { + input: "command arg1 arg2\targ3", + expectedCommand: `command`, + expectedArgs: []string{`arg1`, `arg2`, "arg3"}, + expectedErrContent: "", + }, + // Test case 10 - command with unclosed quotes + { + input: `command "arg1 arg2`, + expectedCommand: "", + expectedArgs: nil, + expectedErrContent: parseErrorContent, + }, + // Test case 11 - command with unclosed quotes + { + input: `command 'arg1 arg2"`, + expectedCommand: "", + expectedArgs: nil, + expectedErrContent: parseErrorContent, + }, + } + + for i, test := range tests { + errorPrefix := fmt.Sprintf("Test [%d]: ", i) + errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input) + actualCommand, actualArgs, actualErr := SplitCommandAndArgs(test.input) + + // test if error matches expectation + if test.expectedErrContent != "" { + if actualErr == nil { + t.Errorf(errorPrefix+"Expected error with content [%s], found no error."+errorSuffix, test.expectedErrContent) + } else if !strings.Contains(actualErr.Error(), test.expectedErrContent) { + t.Errorf(errorPrefix+"Expected error with content [%s], found [%v]."+errorSuffix, test.expectedErrContent, actualErr) + } + } else if actualErr != nil { + t.Errorf(errorPrefix+"Expected no error, found [%v]."+errorSuffix, actualErr) + } + + // test if command matches + if test.expectedCommand != actualCommand { + t.Errorf("Expected command: [%s], actual: [%s]."+errorSuffix, test.expectedCommand, actualCommand) + } + + // test if arguments match + if len(test.expectedArgs) != len(actualArgs) { + t.Errorf("Wrong number of arguments! Expected [%v], actual [%v]."+errorSuffix, test.expectedArgs, actualArgs) + } + + for j, actualArg := range actualArgs { + expectedArg := test.expectedArgs[j] + if actualArg != expectedArg { + t.Errorf(errorPrefix+"Argument at position [%d] differ! Expected [%s], actual [%s]"+errorSuffix, j, expectedArg, actualArg) + } + } + } +} From 6717edcb87c632d82695a77e9046501f21416d2d Mon Sep 17 00:00:00 2001 From: Matt Holt Date: Tue, 13 Oct 2015 09:52:47 -0600 Subject: [PATCH 2/4] Add AppVeyor badge Distinguish between windows and linux builds --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44fd023b..917e094c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Caddy](https://caddyserver.com/resources/images/caddy-boxed.png)](https://caddyserver.com) -[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/mholt/caddy) [![Build Status](https://img.shields.io/travis/mholt/caddy.svg?style=flat-square)](https://travis-ci.org/mholt/caddy) +[![Documentation](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/mholt/caddy) [![Linux Build Status](https://img.shields.io/travis/mholt/caddy.svg?style=flat-square&label=linux+build)](https://travis-ci.org/mholt/caddy) [![Windows Build Status](https://img.shields.io/appveyor/ci/mholt/caddy.svg?style=flat-square&label=windows+build)](https://ci.appveyor.com/project/mholt/caddy) Caddy is a lightweight, general-purpose web server for Windows, Mac, Linux, BSD, and [Android](https://github.com/mholt/caddy/wiki/Running-Caddy-on-Android). It is a capable alternative to other popular and easy to use web servers. From f122b3bbdf6159ec6c87a435cac3bce381f30c3e Mon Sep 17 00:00:00 2001 From: Makpoc Date: Tue, 13 Oct 2015 23:35:24 +0300 Subject: [PATCH 3/4] Fix failing test (windows) - simulate an error by executing stat on a filename with zero-byte in it. Fix cleanup of created files after the tests. --- config/setup/root_test.go | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/config/setup/root_test.go b/config/setup/root_test.go index f34e05d2..ff3dad3f 100644 --- a/config/setup/root_test.go +++ b/config/setup/root_test.go @@ -7,6 +7,7 @@ import ( "path/filepath" "strings" "testing" + "runtime" ) func TestRoot(t *testing.T) { @@ -26,10 +27,13 @@ func TestRoot(t *testing.T) { if err != nil { t.Fatalf("BeforeTest: Failed to create temp file for testing! Error was: %v", err) } - defer os.Remove(existingFile.Name()) - - unaccessiblePath := filepath.Join(existingFile.Name(), "some_name") + defer func () { + existingFile.Close() + os.Remove(existingFile.Name()) + }() + unaccessiblePath := getInaccessibleOsDependentPath(existingFile.Name()) + tests := []struct { input string shouldErr bool @@ -60,8 +64,9 @@ func TestRoot(t *testing.T) { for i, test := range tests { c := NewTestController(test.input) mid, err := Root(c) + if test.shouldErr && err == nil { - t.Errorf("Test %d: Expected error but found nil for input %s", i, test.input) + t.Errorf("Test %d: Expected error but found %s for input %s", i, err, test.input) } if err != nil { @@ -97,3 +102,12 @@ func getTempDirPath() (string, error) { return tempDir, nil } + +func getInaccessibleOsDependentPath(file string) string{ + if runtime.GOOS == "windows"{ + return filepath.Join("C:", "file\x00name")// 0 byte breaks the lstat syscall + } else { + // TODO - check if the zero-byte filename works for linux only. If it does - use it instead + return filepath.Join(file, "some_name") + } +} \ No newline at end of file From 6af26e23066f646181af4c73511477137b27e868 Mon Sep 17 00:00:00 2001 From: makpoc Date: Wed, 14 Oct 2015 09:35:50 +0300 Subject: [PATCH 4/4] Use null byte in filename to simulate 'unable to access' on both windows and linux --- config/setup/root_test.go | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/config/setup/root_test.go b/config/setup/root_test.go index ff3dad3f..8b38e6d0 100644 --- a/config/setup/root_test.go +++ b/config/setup/root_test.go @@ -7,7 +7,6 @@ import ( "path/filepath" "strings" "testing" - "runtime" ) func TestRoot(t *testing.T) { @@ -27,13 +26,13 @@ func TestRoot(t *testing.T) { if err != nil { t.Fatalf("BeforeTest: Failed to create temp file for testing! Error was: %v", err) } - defer func () { + defer func() { existingFile.Close() os.Remove(existingFile.Name()) }() - unaccessiblePath := getInaccessibleOsDependentPath(existingFile.Name()) - + inaccessiblePath := getInaccessiblePath(existingFile.Name()) + tests := []struct { input string shouldErr bool @@ -52,7 +51,7 @@ func TestRoot(t *testing.T) { `root `, true, "", parseErrContent, }, { - fmt.Sprintf(`root %s`, unaccessiblePath), true, "", unableToAccessErrContent, + fmt.Sprintf(`root %s`, inaccessiblePath), true, "", unableToAccessErrContent, }, { fmt.Sprintf(`root { @@ -103,11 +102,7 @@ func getTempDirPath() (string, error) { return tempDir, nil } -func getInaccessibleOsDependentPath(file string) string{ - if runtime.GOOS == "windows"{ - return filepath.Join("C:", "file\x00name")// 0 byte breaks the lstat syscall - } else { - // TODO - check if the zero-byte filename works for linux only. If it does - use it instead - return filepath.Join(file, "some_name") - } -} \ No newline at end of file +func getInaccessiblePath(file string) string { + // null byte in filename is not allowed on Windows AND unix + return filepath.Join("C:", "file\x00name") +}