// Testing rule: NEVER_ALLOCATED_MAP_WITH_NEW // Allocating maps with new returns a nil pointer, therefor one should use make. func TestDetectionOfAllocatingMapWithNew(t *testing.T) { expectedViolations, _ := linter.DetectViolations("./testcode/newmap") actualViolations := []actualViolation{ {SrcLine: 11, Type: linter.MAP_ALLOCATED_WITH_NEW}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
// GitHub Issue #4 list a scenario where the tool detects a false positive of rule ERROR_IGNORED. // This test verifies correction of the behaviour. func TestDetectionOfErrorIgnoredInReturn(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/errorinreturn") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{} if len(expectedViolations) > 0 { // Only verify violations if there is more than one file containing violations! if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } } }
// Testing rule: EMPTY_IF_BODY // Empty if-bodies are unnecessary and ineffective. func TestDetectionOfEmptyIfBody(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/emptyifbody") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 11, Type: linter.EMPTY_IF_BODY}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
// Testing rule: RACE_CONDITION // Races will occur, since multiple Go-routines will share the same counter variable. func TestDetectionOfRacesInLoopClosures(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/threadlooping") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 18, Type: linter.RACE_CONDITION}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
func TestDetectionOfHighCyclomatiComplexity(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/cyclomaticomplexity") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 13, Type: linter.CYCLOMATIC_COMPLEXITY}, } if len(expectedViolations) > 0 { // Only verify violations if there is more than one file containing violations! if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } } }
// Testing rule: GOTO_USED // Jumping around in the code using GOTO (including BREAK, CONTINUE, GOTO, FALLTHROUGH) // is considered confusing and harmful. func TestDetectionOfLabeledBranching(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/labeledbranch") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 15, Type: linter.GOTO_USED}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
// Testing rule: RETURN_KILLS_CODE // One should never return unconditionally, except from the last statement in a func or method. func TestDetectionOfEarlyReturn(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/earlyreturn") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 13, Type: linter.RETURN_KILLS_CODE}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
// Printing from fmt package is not thread safe and should be avoided in production and detected! // Testing rule: FMT_PRINTING func TestDetectionOfPrintingFromFmtPackage(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/fmtprinting") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 11, Type: linter.FMT_PRINTING}, {SrcLine: 12, Type: linter.FMT_PRINTING}, {SrcLine: 13, Type: linter.FMT_PRINTING}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
// Testing rule: ERROR_IGNORED // Errors should never be ignored, might lead to program crashes. func TestDetectionOfIgnoredErrors(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/errorignored") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 16, Type: linter.ERROR_IGNORED}, {SrcLine: 17, Type: linter.ERROR_IGNORED}, {SrcLine: 22, Type: linter.ERROR_IGNORED}, {SrcLine: 27, Type: linter.ERROR_IGNORED}, {SrcLine: 35, Type: linter.ERROR_IGNORED}, {SrcLine: 51, Type: linter.ERROR_IGNORED}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
// Testing rule: CONDITION_EVALUATED_STATICALLY // Condition that can be evaluated statically are wasted and performance-reducing. func TestDetectionOfConditionEvaluatedStatically(t *testing.T) { expectedViolations, err := linter.DetectViolations("./testcode/staticconditions") if err != nil { t.Fatal(err) } actualViolations := []actualViolation{ {SrcLine: 14, Type: linter.CONDITION_EVALUATED_STATICALLY}, {SrcLine: 18, Type: linter.CONDITION_EVALUATED_STATICALLY}, {SrcLine: 22, Type: linter.CONDITION_EVALUATED_STATICALLY}, {SrcLine: 26, Type: linter.CONDITION_EVALUATED_STATICALLY}, //{SrcLine: 32, Type: linter.CONDITION_EVALUATED_STATICALLY}, TODO: Possible to statically trace a variable value? {SrcLine: 38, Type: linter.CONDITION_EVALUATED_STATICALLY}, {SrcLine: 41, Type: linter.CONDITION_EVALUATED_STATICALLY}, } if len(expectedViolations) <= 0 { t.Fatal("There is no functions containing violations.") } if err := verifyViolations(expectedViolations[0].Violations[0].Violations, actualViolations); err != nil { t.Fatal(err) } }
func main() { flag.Parse() if flag.NFlag() < 1 { flag.Usage() } // Option dir selected. if len(*sourceRootDir) > 0 { start := time.Now() goPackageViolations, err := linter.DetectViolations(*sourceRootDir) // Start the analysis. if err != nil { log.Fatal(err) } timeUsed := time.Since(start) // Direct output to console as JSON. if *jsonOutput && len(goPackageViolations) > 0 { // Aggregate GoFiles to JSON marshalling. violationsMap := make(map[string]*linter.GoFile) for _, goPackage := range goPackageViolations { for _, goFile := range goPackage.Violations { if gf, ok := violationsMap[goFile.FilePath]; ok { gf.Violations = append(gf.Violations, goFile.Violations...) } else { violationsMap[goFile.FilePath] = goFile } } } // Convert to list for JSON marshalling. var violations []*linter.GoFile for _, value := range violationsMap { violations = append(violations, value) } json, err := json.MarshalIndent(violations, "", "\t") if err != nil { log.Fatal(err) } if _, err := os.Stdout.Write(json); err != nil { panic(err) } } else { log.SetOutput(os.Stdout) // We want to send output to stdout, instead of Stderr. numberOfViolations := 0 linesOfCode := 0 linesOfComments := 0 // Nicely print the output to the console. log.Println("-----------------------------------------------------------------------------------------------") for _, goPackage := range goPackageViolations { log.Printf("PACKAGE: %s (%s)", goPackage.Pack.Name, goPackage.Path) for _, goFile := range goPackage.Violations { log.Printf("\tViolations in %s :", filepath.Base(goFile.FilePath)) for i, vio := range goFile.Violations { log.Printf("\t\t%d) %s\n", i, vio) } linesOfCode += goFile.LinesOfCode linesOfComments += goFile.LinesOfComments } numberOfViolations += len(goPackage.Violations) log.Println("-----------------------------------------------------------------------------------------------") } log.Println("## ANALYSIS SUMMARY ##") log.Printf("Total %d violations found!\n", numberOfViolations) log.Printf("Total number of Go files: %d\n", countGoFiles(*sourceRootDir)) log.Printf("Total lines of code (LOC): %d\n", linesOfCode) log.Printf("Total lines of comments: %d\n", linesOfComments) log.Printf("Total time used: %s\n", timeUsed) log.Printf("For rule details: %s\n", globalvars.WIKI_PAGE) } } // Print help. if *printHelp { flag.Usage() } }