diff --git a/htmltest/htmltest.go b/htmltest/htmltest.go index 8bed808..a1c8c09 100644 --- a/htmltest/htmltest.go +++ b/htmltest/htmltest.go @@ -278,3 +278,8 @@ func (hT *HTMLTest) CountErrors() int { func (hT *HTMLTest) CountDocuments() int { return len(hT.documentStore.Documents) } + +// CountDocuments : Return number of documents in hT document store +func (hT *HTMLTest) FormatIssueStats() string { + return hT.issueStore.FormatIssueStats() +} diff --git a/issues/issue_store.go b/issues/issue_store.go index f572417..92fc60f 100644 --- a/issues/issue_store.go +++ b/issues/issue_store.go @@ -7,6 +7,7 @@ import ( "github.com/wjdp/htmltest/htmldoc" "github.com/wjdp/htmltest/output" "io/ioutil" + "sort" "strings" "sync" ) @@ -125,3 +126,68 @@ func (iS *IssueStore) DumpIssues(force bool) { } fmt.Println(">>>>>>>>>>>>>>>>>>>>>>>>") } + +type IssueStats struct { + // How many issues of each level + TotalByLevel map[int]int + // Collect errors against count + ErrorsByMessage map[string]int + // Collect warnings against count + WarningsByMessage map[string]int +} + +// GetIssueStats : Get stats on issues in the store. +func (iS *IssueStore) GetIssueStats() IssueStats { + stats := IssueStats{TotalByLevel: make(map[int]int), ErrorsByMessage: make(map[string]int), WarningsByMessage: make(map[string]int)} + for _, issue := range iS.issues { + stats.TotalByLevel[issue.Level]++ + if issue.Level == LevelError { + stats.ErrorsByMessage[issue.Message]++ + } + if issue.Level == LevelWarning { + stats.WarningsByMessage[issue.Message]++ + } + } + return stats +} + +func formatMessageCounts(messageCounts map[string]int) string { + keySlice := make([]string, 0) + for key, _ := range messageCounts { + keySlice = append(keySlice, key) + } + sort.Strings(keySlice) + + var s string + for _, message := range keySlice { + s += fmt.Sprintf(" %d \"%s\"\n", messageCounts[message], message) + } + return s +} + +// FormatIssueStats : Return formatted stats on issues in the store. +func (iS *IssueStore) FormatIssueStats() string { + formattedStats := "" + stats := iS.GetIssueStats() + if iS.logLevel <= LevelError { + formattedStats += fmt.Sprintln(" Errors: ", stats.TotalByLevel[LevelError]) + } + if iS.logLevel <= LevelWarning { + formattedStats += fmt.Sprintln(" Warnings:", stats.TotalByLevel[LevelWarning]) + } + if iS.logLevel <= LevelInfo { + formattedStats += fmt.Sprintln(" Infos: ", stats.TotalByLevel[LevelInfo]) + } + if iS.logLevel <= LevelDebug { + formattedStats += fmt.Sprintln(" Debugs: ", stats.TotalByLevel[LevelDebug]) + } + if (iS.logLevel <= LevelError) && (len(stats.ErrorsByMessage) > 0) { + formattedStats += fmt.Sprintln(" Errors by message:") + formattedStats += formatMessageCounts(stats.ErrorsByMessage) + } + if (iS.logLevel <= LevelWarning) && (len(stats.WarningsByMessage) > 0) { + formattedStats += fmt.Sprintln(" Warnings by message:") + formattedStats += formatMessageCounts(stats.WarningsByMessage) + } + return formattedStats +} diff --git a/issues/issue_store_test.go b/issues/issue_store_test.go index 4f65af6..7c751e9 100644 --- a/issues/issue_store_test.go +++ b/issues/issue_store_test.go @@ -1,10 +1,12 @@ package issues import ( + "fmt" "github.com/daviddengcn/go-assert" "github.com/wjdp/htmltest/htmldoc" "io/ioutil" "os" + "reflect" "strings" "testing" ) @@ -140,3 +142,111 @@ func ExampleIssueStorePrintDocumentIssuesEmpty() { iS.PrintDocumentIssues(&doc) // Output: } + +func TestGetIssueStats_None(t *testing.T) { + iS := NewIssueStore(LevelError, false) + stats := iS.GetIssueStats() + assert.IsTrue(t, "TotalByLevel", reflect.DeepEqual(stats.TotalByLevel, map[int]int{})) + assert.IsTrue(t, "ErrorsByMessage", reflect.DeepEqual(stats.ErrorsByMessage, map[string]int{})) + assert.IsTrue(t, "WarningsByMessage", reflect.DeepEqual(stats.WarningsByMessage, map[string]int{})) +} + +func addOneError(iS *IssueStore) { + iS.AddIssue(Issue{ + Level: LevelError, + Message: "test", + }) +} + +func addMultipleIssues(iS *IssueStore) { + iS.AddIssue(Issue{ + Level: LevelError, + Message: "test1", + }) + iS.AddIssue(Issue{ + Level: LevelWarning, + Message: "test1", + }) + iS.AddIssue(Issue{ + Level: LevelInfo, + Message: "test1", + }) + iS.AddIssue(Issue{ + Level: LevelDebug, + Message: "test1", + }) + iS.AddIssue(Issue{ + Level: LevelError, + Message: "test2", + }) + iS.AddIssue(Issue{ + Level: LevelError, + Message: "test2", + }) +} + +func TestGetIssueStats_OneError(t *testing.T) { + iS := NewIssueStore(LevelError, false) + addOneError(&iS) + stats := iS.GetIssueStats() + assert.Equals( + t, "TotalByLevel", + fmt.Sprint(stats.TotalByLevel), + fmt.Sprint(map[int]int{LevelError: 1}), + ) + assert.Equals( + t, "ErrorsByMessage", + fmt.Sprint(stats.ErrorsByMessage), + fmt.Sprint(map[string]int{"test": 1}), + ) +} + +func TestGetIssueStats_MultipleIssues(t *testing.T) { + iS := NewIssueStore(LevelError, false) + addMultipleIssues(&iS) + stats := iS.GetIssueStats() + assert.Equals(t, + "TotalByLevel", + fmt.Sprint(stats.TotalByLevel), + fmt.Sprint(map[int]int{LevelError: 3, LevelWarning: 1, LevelInfo: 1, LevelDebug: 1})) + assert.Equals( + t, "ErrorsByMessage", + fmt.Sprint(stats.ErrorsByMessage), + fmt.Sprint(map[string]int{"test1": 1, "test2": 2}), + ) + assert.Equals( + t, "WarningsByMessage", + fmt.Sprint(stats.WarningsByMessage), + fmt.Sprint(map[string]int{"test1": 1}), + ) +} + +func TestFormatIssueStats_None(t *testing.T) { + iS := NewIssueStore(LevelError, false) + assert.Equals(t, "FormatIssueStats", iS.FormatIssueStats(), " Errors: 0\n") +} + +func TestFormatIssueStats_Multiple_AtLogLevelError(t *testing.T) { + iS := NewIssueStore(LevelError, false) + addMultipleIssues(&iS) + const expected = ` Errors: 3 + Errors by message: + 1 "test1" + 2 "test2" +` + assert.Equals(t, "FormatIssueStats", iS.FormatIssueStats(), expected) +} + +func TestFormatIssueStats_Multiple_AtLogLevelWarning(t *testing.T) { + iS := NewIssueStore(LevelWarning, false) + addMultipleIssues(&iS) + const expected = ` Errors: 3 + Warnings: 1 + Errors by message: + 1 "test1" + 2 "test2" + Warnings by message: + 1 "test1" +` + assert.Equals(t, "FormatIssueStats", iS.FormatIssueStats(), expected) +} diff --git a/main.go b/main.go index 5c54fad..406a9b5 100644 --- a/main.go +++ b/main.go @@ -169,25 +169,36 @@ func run(options optsMap) int { timeEnd := time.Now() numErrors := hT.CountErrors() + documentCount := hT.CountDocuments() + + if !fileMode && documentCount == 0 { + color.Set(color.FgHiYellow) + fmt.Printf("No documents found in '%s'\n", options["DirectoryPath"]) + color.Unset() + return 2 + } if numErrors == 0 { color.Set(color.FgHiGreen) fmt.Println("✔✔✔ passed in", timeEnd.Sub(timeStart)) if !fileMode { - fmt.Println("tested", hT.CountDocuments(), "documents") + fmt.Println("tested", documentCount, "documents") } color.Unset() return 0 } - color.Set(color.FgHiRed) fmt.Println(cmdSeparator) + color.Set(color.FgHiRed) fmt.Println("✘✘✘ failed in", timeEnd.Sub(timeStart)) - if fileMode { - fmt.Println(numErrors, "errors") - } else { - fmt.Println(numErrors, "errors in", hT.CountDocuments(), "documents") + if !fileMode { + fmt.Println(output.Pluralise( + documentCount, + fmt.Sprint("tested ", documentCount, " document"), + fmt.Sprint("tested ", documentCount, " documents"), + )) } + fmt.Printf(hT.FormatIssueStats()) color.Unset() return 1 diff --git a/output/strings.go b/output/strings.go new file mode 100644 index 0000000..0207c99 --- /dev/null +++ b/output/strings.go @@ -0,0 +1,9 @@ +package output + +// Pluralise: a dumb pluraliser that only works for English. +func Pluralise(n int, singular string, plural string) string { + if n == 1 { + return singular + } + return plural +} diff --git a/output/strings_test.go b/output/strings_test.go new file mode 100644 index 0000000..16a68b0 --- /dev/null +++ b/output/strings_test.go @@ -0,0 +1,11 @@ +package output + +import ( + "github.com/daviddengcn/go-assert" + "testing" +) + +func TestPluralise(t *testing.T) { + assert.Equals(t, "singular", Pluralise(1, "1 apple", "2 apples"), "1 apple") + assert.Equals(t, "plural", Pluralise(2, "1 apple", "2 apples"), "2 apples") +}