diff --git a/common_test.go b/common_test.go new file mode 100644 index 0000000..30c6a75 --- /dev/null +++ b/common_test.go @@ -0,0 +1,13 @@ +package jsh + +import "testing" + +func TestToJson(t *testing.T) { + fixture := JshOutput{[]string{}, []string{}} + json := fixture.ToJson() + expected := "{\"StdOut\":[],\"StdErr\":[]}" + actual := *json + if actual != expected { + t.Error("Empty fixture did not match:\n%s != %s", expected, actual) + } +} diff --git a/process_test.go b/process_test.go new file mode 100644 index 0000000..d7ddeb9 --- /dev/null +++ b/process_test.go @@ -0,0 +1,28 @@ +package jsh + +import "testing" + +func TestNewProcess(t *testing.T) { + tooManyArgs := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"} + _, err := NewProcess(tooManyArgs) + if err == nil { + t.Errorf("Passing 12 strings should raise an error") + } + + tooFewArgs := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"} + _, err = NewProcess(tooFewArgs) + if err == nil { + t.Errorf("Passing 10 strings should raise an error") + } + + justRightArgs := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"} + proc, err := NewProcess(justRightArgs) + if err != nil { + t.Errorf("Passing 11 strings should not raise an error") + } + expected := Process{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"} + actual := *proc + if expected != actual { + t.Errorf("Proc was incorrectly generated:\n%s != %s", expected, actual) + } +} diff --git a/ps/main.go b/ps/main.go index 9edfc2a..730a844 100644 --- a/ps/main.go +++ b/ps/main.go @@ -4,49 +4,25 @@ import ( "flag" "fmt" "jsh" - "math" "os" "os/exec" "strings" - "unicode" ) -// FieldsN slices s into substrings after each instance of a whitespace -// character (according to unicode.IsSpace) and returns a slice of those -// substrings. The slice's length is guaranteed to be at most maxN, and -// all leftover fields are put into the last substring. -func FieldsN(s string, maxN int) []string { - // First count the fields. - n := 0 - inField := false - for _, rune := range s { - wasInField := inField - inField = !unicode.IsSpace(rune) - if inField && !wasInField { - n++ - } - } - n = int(math.Min(float64(n), float64(maxN))) +// Converts raw output of "ps" into a slice of Process objects +func PsOutputToProcesses(out string) *[]jsh.Process { + processes := []jsh.Process{} + lines := strings.Split(out, "\n") + header, procs := lines[0], lines[1:] + numFields := len(strings.Fields(header)) - // Now create them. - a := make([]string, n) - na := 0 - fieldStart := -1 // Set to -1 when looking for start of field. - for i, rune := range s { - if unicode.IsSpace(rune) && na+1 < maxN { - if fieldStart >= 0 { - a[na] = s[fieldStart:i] - na++ - fieldStart = -1 - } - } else if fieldStart == -1 { - fieldStart = i + for _, proc := range procs { + p, err := jsh.NewProcess(jsh.FieldsN(proc, numFields)) + if err == nil { + processes = append(processes, *p) } } - if fieldStart >= 0 { // Last field might end at EOF. - a[na] = s[fieldStart:] - } - return a + return &processes } // Falls back to procps-ng passing the space-separated arguments in the given @@ -68,27 +44,11 @@ func fallbackCompletely() *[]byte { return fallbackCompletelyWithArgs(nil) } -// Converts raw output of "ps" into a slice of Process objects -func psOutputToProcesses(out string) *[]jsh.Process { - processes := []jsh.Process{} - lines := strings.Split(out, "\n") - header, procs := lines[0], lines[1:] - numFields := len(strings.Fields(header)) - - for _, proc := range procs { - p, err := jsh.NewProcess(FieldsN(proc, numFields)) - if err == nil { - processes = append(processes, *p) - } - } - return &processes -} - func runJsonMode() { // Run procps-ng "ps" with full output psOut := string(*fallbackCompletelyWithArgs([]string{"auxww"})) - processesPtr := psOutputToProcesses(psOut) + processesPtr := PsOutputToProcesses(psOut) finalOut := jsh.JshOutput{*processesPtr, []string{}} fmt.Printf("%s", *finalOut.ToJson()) } diff --git a/ps/main_test.go b/ps/main_test.go new file mode 100644 index 0000000..a2b92b7 --- /dev/null +++ b/ps/main_test.go @@ -0,0 +1,7 @@ +package main + +import "testing" + +func TestPsOutputToProcesses(t *testing.T) { + +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..19cc85e --- /dev/null +++ b/utils.go @@ -0,0 +1,44 @@ +package jsh + +import ( + "math" + "unicode" +) + +// FieldsN slices s into substrings after each instance of a whitespace +// character (according to unicode.IsSpace) and returns a slice of those +// substrings. The slice's length is guaranteed to be at most maxN, and +// all leftover fields are put into the last substring. +func FieldsN(s string, maxN int) []string { + // First count the fields. + n := 0 + inField := false + for _, rune := range s { + wasInField := inField + inField = !unicode.IsSpace(rune) + if inField && !wasInField { + n++ + } + } + n = int(math.Min(float64(n), float64(maxN))) + + // Now create them. + a := make([]string, n) + na := 0 + fieldStart := -1 // Set to -1 when looking for start of field. + for i, rune := range s { + if unicode.IsSpace(rune) && na+1 < maxN { + if fieldStart >= 0 { + a[na] = s[fieldStart:i] + na++ + fieldStart = -1 + } + } else if fieldStart == -1 { + fieldStart = i + } + } + if fieldStart >= 0 { // Last field might end at EOF. + a[na] = s[fieldStart:] + } + return a +} diff --git a/utils_test.go b/utils_test.go new file mode 100644 index 0000000..8252fce --- /dev/null +++ b/utils_test.go @@ -0,0 +1,14 @@ +package jsh + +import "testing" + +func TestFieldsN(t *testing.T) { + testString := "Potatoes are the best strings for testing." + for i := 1; i <= 10; i++ { + fields := FieldsN(testString, i) + fieldsLength := len(fields) + if fieldsLength > i { + t.Errorf("Expected at most %d fields, got %d", i, fieldsLength) + } + } +}