Merge lp:~xavi-garcia-mena/go-unityscopes/utils-command into lp:go-unityscopes/v2

Proposed by Xavi Garcia
Status: Needs review
Proposed branch: lp:~xavi-garcia-mena/go-unityscopes/utils-command
Merge into: lp:go-unityscopes/v2
Diff against target: 304 lines (+287/-0)
3 files modified
utils/command/command.go (+109/-0)
utils/command/command_test.go (+144/-0)
utils/testutils/testutils.go (+34/-0)
To merge this branch: bzr merge lp:~xavi-garcia-mena/go-unityscopes/utils-command
Reviewer Review Type Date Requested Status
Unity API Team Pending
Review via email: mp+265832@code.launchpad.net

Commit message

Adding the initial utils directory to go-unityscopes.
It includes 2 packages:
* testutils
    With common test utilities
* command
    For executing system commands capturing the output, stderror and exit code.

Description of the change

Adding the initial utils directory to go-unityscopes.
It includes 2 packages:
* testutils
    With common test utilities
* command
    For executing system commands capturing the output, stderror and exit code.

To post a comment you must log in.

Unmerged revisions

70. By Xavi Garcia

Added utils directory with tools for building. Added common test utils package. Added utils command package to run system commands

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'utils'
=== added directory 'utils/command'
=== added file 'utils/command/command.go'
--- utils/command/command.go 1970-01-01 00:00:00 +0000
+++ utils/command/command.go 2015-07-24 14:19:30 +0000
@@ -0,0 +1,109 @@
1package command
2
3import (
4 "bytes"
5 "fmt"
6 "os"
7 "os/exec"
8 "strings"
9 "syscall"
10)
11
12// Command represents a command to be executed, checking its exit code for errors
13type Command struct {
14 ExitCode int
15 Cmd string
16 Output string
17}
18
19// BuildCommand splits the received string command and returns the first item (command) and the arguments.
20func (c Command) BuildCommand(cmd string) (string, []string) {
21 items := strings.Split(cmd, " ")
22 return items[0], items[1:]
23}
24
25// captureOutput is a helper method to capture stderr and, depending on the give boolean parameter, capures stdout as well
26// Parameters:
27// cmd: the command to capture from
28// captureOutput: if true, captures the stdout, it prints the output to the system stdout otherwise
29// Return: the stderr and stdout buffers created to capture the command output and errors.
30func (c *Command) captureOutput(cmd *exec.Cmd, captureOutput bool) (*bytes.Buffer, *bytes.Buffer) {
31 cmdStdErr := &bytes.Buffer{}
32 cmdStdOut := &bytes.Buffer{}
33 cmd.Stderr = cmdStdErr
34 cmd.Stdin = os.Stdin
35 if captureOutput == true {
36 cmd.Stdout = cmdStdOut
37 } else {
38 cmd.Stdout = os.Stdout
39 }
40 return cmdStdOut, cmdStdErr
41}
42
43// runCommand is the method that runs the command and checks the exit code.
44// Parameters:
45// name: system command to be executed
46// captureOutput: if set to true it captures and stores the output, otherwise it uses os.Stdout
47// arg: arguments to the command to be executed
48//
49// Return:
50// If the exit code matches the expected nil is returned, otherwise it returns an error.
51// If the command itself returns an error (command does not exists in the system or similar)
52// it also returns an error.
53func (c *Command) runCommand(name string, captureOutput bool, arg ...string) error {
54 c.Cmd = fmt.Sprintf("%s %s", name, strings.Join(arg, " "))
55 cmd := exec.Command(name, arg...)
56 cmdStdOut, cmdStdErr := c.captureOutput(cmd, captureOutput)
57 err := cmd.Run()
58 errorMessage := ""
59 c.Output = ""
60 if err != nil {
61 // retrieve exit code and error message
62 if exitError, ok := err.(*exec.ExitError); ok {
63 waitStatus := exitError.Sys().(syscall.WaitStatus)
64 c.ExitCode = waitStatus.ExitStatus()
65 // get the error message from the command stderr
66 errorMessage = fmt.Sprintf("%s", cmdStdErr.Bytes())
67 } else {
68 // the command did not execute properly
69 c.ExitCode = -1
70 return fmt.Errorf("Error running command. %s", err.Error())
71 }
72 } else {
73 c.ExitCode = 0
74 }
75 if captureOutput {
76 c.Output = fmt.Sprintf("%s", cmdStdOut.Bytes())
77 }
78 if c.ExitCode != 0 {
79 return fmt.Errorf("Error exit code %d. Error message: %s", c.ExitCode, errorMessage)
80 } else {
81 return nil
82 }
83}
84
85// Execute runs a system command and checks that its exit code is 0.
86// Parameters:
87// cmd: system command to be executed
88//
89// Return:
90// If the exit code is 0 nil is returned, otherwise it returns an error.
91// If the command itself returns an error (command does not exists in the system or similar)
92// it also returns an error.
93func (c *Command) Execute(cmd string) error {
94 name, arg := c.BuildCommand(cmd)
95 return c.runCommand(name, true, arg...)
96}
97
98// ExecuteWithStdout runs a system command, checks that its exit code is 0, and prints the output of the command to stdout.
99// Parameters:
100// cmd: system command to be executed
101//
102// Return:
103// If the exit code is 0 nil is returned, otherwise it returns an error.
104// If the command itself returns an error (command does not exists in the system or similar)
105// it also returns an error.
106func (c *Command) ExecuteWithStdout(cmd string) error {
107 name, arg := c.BuildCommand(cmd)
108 return c.runCommand(name, false, arg...)
109}
0110
=== added file 'utils/command/command_test.go'
--- utils/command/command_test.go 1970-01-01 00:00:00 +0000
+++ utils/command/command_test.go 2015-07-24 14:19:30 +0000
@@ -0,0 +1,144 @@
1package command_test
2
3import (
4 . "gopkg.in/check.v1"
5 "io/ioutil"
6 "launchpad.net/go-unityscopes/v2/utils/command"
7 "launchpad.net/go-unityscopes/v2/utils/testutils"
8 "os"
9 "testing"
10)
11
12const (
13 TEST_DIR = "testDir"
14)
15
16type S struct{}
17
18func init() {
19 Suite(&S{})
20}
21
22func TestAll(t *testing.T) {
23 TestingT(t)
24}
25
26func setupTestDir() error {
27 err := os.RemoveAll(TEST_DIR)
28 if err != nil {
29 return err
30 }
31 err = os.Mkdir(TEST_DIR, 0777)
32 if err != nil {
33 return err
34 }
35 err = ioutil.WriteFile("testDir/test_1.txt", []byte("blah"), 0777)
36 if err != nil {
37 return err
38 }
39 err = ioutil.WriteFile("testDir/test_2.txt", []byte("blah"), 0777)
40 if err != nil {
41 return err
42 }
43 return nil
44}
45
46func tearDownTestDir() error {
47 return os.RemoveAll(TEST_DIR)
48}
49
50func (s *S) TestCommandIsExecutedOk(c *C) {
51 err := setupTestDir()
52 c.Assert(err, IsNil)
53
54 var cmd command.Command
55 err = cmd.Execute("ls ./" + TEST_DIR)
56 c.Check(err, IsNil)
57 c.Check(cmd.Cmd, Equals, "ls ./"+TEST_DIR)
58 c.Check(cmd.ExitCode, Equals, 0)
59 c.Check(cmd.Output, Equals, ""+"test_1.txt\n"+"test_2.txt\n")
60
61 err = tearDownTestDir()
62 c.Check(err, IsNil)
63}
64
65func (s *S) TestCommandIsExecutedOkWithStdOut(c *C) {
66 err := setupTestDir()
67 c.Assert(err, IsNil)
68
69 var capture testutils.CaptureStdOut
70 // capture stdout to instpect that the result of the print method is correct.
71 capture.Capture()
72
73 var cmd command.Command
74 err = cmd.ExecuteWithStdout("ls ./" + TEST_DIR)
75 c.Check(err, IsNil)
76 c.Check(cmd.Cmd, Equals, "ls ./"+TEST_DIR)
77 c.Check(cmd.ExitCode, Equals, 0)
78 c.Check(cmd.Output, Equals, "")
79
80 capture.StopCapture()
81 c.Check(capture.GetCapuredOut(), Equals, ""+"test_1.txt\n"+"test_2.txt\n")
82
83 err = tearDownTestDir()
84 c.Check(err, IsNil)
85}
86
87func (s *S) TestCommandFailedBadParameters(c *C) {
88 var cmd command.Command
89 err := cmd.Execute("ls ./blah")
90 c.Assert(err, Not(Equals), nil)
91 c.Check(cmd.Cmd, Equals, "ls ./blah")
92 c.Check(cmd.ExitCode, Equals, 2)
93 c.Check(err.Error(), Equals, "Error exit code 2. Error message: ls: cannot access ./blah: No such file or directory\n")
94}
95
96func (s *S) TestCommandFailedBadParametersWithStdout(c *C) {
97 var cmd command.Command
98 err := cmd.ExecuteWithStdout("ls ./blah")
99 c.Assert(err, Not(Equals), nil)
100 c.Check(cmd.Cmd, Equals, "ls ./blah")
101 c.Check(cmd.ExitCode, Equals, 2)
102 c.Check(cmd.Output, Equals, "")
103 c.Check(err.Error(), Equals, "Error exit code 2. Error message: ls: cannot access ./blah: No such file or directory\n")
104}
105
106func (s *S) TestCommandFailedCommandDoesNotExist(c *C) {
107 var cmd command.Command
108 err := cmd.Execute("lslslslslsnot-exists ./blah")
109 c.Assert(err, Not(Equals), nil)
110 c.Check(cmd.Cmd, Equals, "lslslslslsnot-exists ./blah")
111 c.Check(cmd.ExitCode, Equals, -1)
112 c.Check(err.Error(), Equals, "Error running command. exec: \"lslslslslsnot-exists\": executable file not found in $PATH")
113}
114
115func (s *S) TestCommandFailedCommandDoesNotExistWithStdOut(c *C) {
116 var cmd command.Command
117 err := cmd.ExecuteWithStdout("lslslslslsnot-exists ./blah")
118 c.Assert(err, Not(Equals), nil)
119 c.Check(cmd.Cmd, Equals, "lslslslslsnot-exists ./blah")
120 c.Check(cmd.ExitCode, Equals, -1)
121 c.Check(cmd.Output, Equals, "")
122 c.Check(err.Error(), Equals, "Error running command. exec: \"lslslslslsnot-exists\": executable file not found in $PATH")
123}
124
125func (s *S) TestSplitCommandOK(c *C) {
126 var cmd command.Command
127 name, args := cmd.BuildCommand("ls -la ./")
128 c.Check(name, Equals, "ls")
129 c.Check(args, DeepEquals, []string{"-la", "./"})
130}
131
132func (s *S) TestSplitCommandNoArgs(c *C) {
133 var cmd command.Command
134 name, args := cmd.BuildCommand("ls")
135 c.Check(name, Equals, "ls")
136 c.Check(args, DeepEquals, []string{})
137}
138
139func (s *S) TestSplitCommandEmpty(c *C) {
140 var cmd command.Command
141 name, args := cmd.BuildCommand("")
142 c.Check(name, Equals, "")
143 c.Check(args, DeepEquals, []string{})
144}
0145
=== added directory 'utils/testutils'
=== added file 'utils/testutils/testutils.go'
--- utils/testutils/testutils.go 1970-01-01 00:00:00 +0000
+++ utils/testutils/testutils.go 2015-07-24 14:19:30 +0000
@@ -0,0 +1,34 @@
1package testutils
2
3import (
4 "bytes"
5 "io"
6 "os"
7)
8
9// CaptureStdOut is a struct that holds pointers to stdout and its redirection in order to capture stdout for some time.
10type CaptureStdOut struct {
11 StdOut *os.File
12 NewOut *os.File
13 NewReader *os.File
14}
15
16// Capture captures the system stdout and stores it for restoring it in the future.
17func (c *CaptureStdOut) Capture() {
18 c.StdOut = os.Stdout
19 c.NewReader, c.NewOut, _ = os.Pipe()
20 os.Stdout = c.NewOut
21}
22
23// StopCapture restores the system stdout
24func (c *CaptureStdOut) StopCapture() {
25 c.NewOut.Close()
26 os.Stdout = c.StdOut
27}
28
29// GetCapuredOut returns the stdout captured as a string.
30func (c CaptureStdOut) GetCapuredOut() string {
31 var buf bytes.Buffer
32 io.Copy(&buf, c.NewReader)
33 return buf.String()
34}

Subscribers

People subscribed via source and target branches

to all changes: