func (s *suite) TestFileModeAndNaming(c *gc.C) { diff, err := diffparser.Parse(s.rawdiff) c.Assert(err, jc.ErrorIsNil) c.Assert(diff.Files, gc.HasLen, 5) for i, expected := range []struct { mode diffparser.FileMode origName string newName string }{ { mode: diffparser.MODIFIED, origName: "file1", newName: "file1", }, { mode: diffparser.DELETED, origName: "file2", newName: "", }, { mode: diffparser.DELETED, origName: "file3", newName: "", }, { mode: diffparser.NEW, origName: "", newName: "file4", }, { mode: diffparser.NEW, origName: "", newName: "newname", }, } { file := diff.Files[i] c.Logf("testing file: %v", file) c.Assert(file.Mode, gc.Equals, expected.mode) c.Assert(file.OrigName, gc.Equals, expected.origName) c.Assert(file.NewName, gc.Equals, expected.newName) } }
func reviewAction(ctx *cli.Context) { // TODO: file args input, as files and dirs var err error var diff *diffparser.Diff // create new diff to filter issues by if ctx.Bool("diff") { diff, err = diffparser.Parse(rawDiff()) if err != nil { oserrf(err.Error()) return } } fileArgs := ctx.Args() var changed *map[string][]int = nil if diff != nil { // if we are diffing, add all files in diff for _, f := range diff.Files { if f.Mode != diffparser.DELETED { fileArgs = append(fileArgs, f.NewName) } } // TODO(waigani) find a better place for this to live. c := diff.Changed() for i, f := range diff.Files { newDiffRootPath := review.GetDiffRootPath(f.NewName) origDiffRootPath := review.GetDiffRootPath(f.OrigName) diff.Files[i].NewName = newDiffRootPath f.OrigName = origDiffRootPath // Need untransformed names to stay in changeset to use diff information in reviewQueue if origDiffRootPath != f.NewName { c[origDiffRootPath] = c[f.NewName] delete(c, f.NewName) } } changed = &c } else if len(fileArgs) == 0 { fileArgs = []string{"."} } // Get this first as it might fail, we want to avoid all other work in that case cfm, err := review.NewConfirmer(ctx, diff) if err != nil { oserrf(err.Error()) return } // Map of project config filenames -> directories they control cfgList := []cfgMap{} // TODO: This loop is now pipelinable too, if we need to further reduce time-to-first-review for _, f := range fileArgs { // Specifically asking for a file that can't be found/read is fatal file, err := os.Open(f) if err != nil { oserrf(err.Error()) return } fi, err := file.Stat() if err != nil { oserrf(err.Error()) return } if fi.IsDir() { filepath.Walk(f, func(relPath string, info os.FileInfo, err error) error { if info.IsDir() { // Ignore folders beginning with '.', except search root // TODO: Flag to turn this behaviour off if len(relPath) > 1 && info.Name()[0] == '.' { return filepath.SkipDir } // TODO: Faster technique for finding cfgPath taking advantage of Walk's depth-first search // This implementation recurses upwards for each found dir cfgPath, _ := tenetCfgPathRecusive(path.Join(relPath, defaultTenetCfgPath)) cfgList = append(cfgList, cfgMap{ path: cfgPath, cfg: nil, dirs: []string{relPath}, files: []string{}, }) } return nil }) } else { cfgPath, _ := tenetCfgPathRecusive(path.Join(filepath.Dir(f), defaultTenetCfgPath)) cfgList = append(cfgList, cfgMap{ path: cfgPath, cfg: nil, dirs: []string{}, files: []string{f}, }) } } // Receiver for errors that can occur during pipeline stages errc := make(chan error, 100000) // Use a channel to read configs with directory mapping configDirs := readCfgs(cfgList, errc) rc, cancelledc := reviewQueue(ctx, configDirs, changed, errc) var count int keptIssuesc := make(chan *api.Issue) // collectedIssues has a huge buffer so we can store all the found issues, // allowing the tenet instances to be stopped. If this buffer is filled, // tenets will not be stopped. They will hang around until there is room // to offload their issues. collectedIssuesc := make(chan *api.Issue, 100000) allIssuesWG := &sync.WaitGroup{} go func() { for i := range collectedIssuesc { if cfm.Confirm(0, i) { keptIssuesc <- i } } close(keptIssuesc) close(errc) }() z: for { select { case r, open := <-rc: if !open && r == nil { break z } allIssuesWG.Add(1) go func(r *tenetReview) { defer r.issuesWG.Done() l: for { select { case i, ok := <-r.issuesc: if !ok && i == nil { allIssuesWG.Done() break l } count++ select { case <-cancelledc: case collectedIssuesc <- i: } } } }(r) } } // Wait for all issues to be read. allIssuesWG.Wait() // then close our collection chan. close(collectedIssuesc) var issues []*api.Issue for i := range keptIssuesc { issues = append(issues, i) } errors := []error{} for err := range errc { errors = append(errors, err) } outputFmt := review.OutputFormat(ctx.String("output-fmt")) // Print errors if any occured if len(errors) > 0 { fmt.Println("The following errors were encounted:") for _, err := range errors { fmt.Printf("%v\n", err) } if outputFmt != "none" { var options string fmt.Println("Do you still wish to output the found issues? [y]es [N]o") fmt.Scanln(&options) switch options { case "y", "Y", "yes": default: return } } } if outputFmt != "none" { output := review.Output(outputFmt, ctx.String("output"), issues) fmt.Print(output) } else { // TODO(waigani) make more informative // TODO(waigani) if !ctx.String("quiet") fmt.Printf("Done! Found %d issues \n", count) } }
func (s *suite) TestHunk(c *gc.C) { diff, err := diffparser.Parse(s.rawdiff) c.Assert(err, jc.ErrorIsNil) c.Assert(diff.Files, gc.HasLen, 5) expectedOrigLines := []diffparser.DiffLine{ { Mode: diffparser.UNCHANGED, Number: 1, Content: "some", Position: 2, }, { Mode: diffparser.UNCHANGED, Number: 2, Content: "lines", Position: 3, }, { Mode: diffparser.REMOVED, Number: 3, Content: "in", Position: 4, }, { Mode: diffparser.UNCHANGED, Number: 4, Content: "file1", Position: 5, }, } expectedNewLines := []diffparser.DiffLine{ { Mode: diffparser.ADDED, Number: 1, Content: "add a line", Position: 1, }, { Mode: diffparser.UNCHANGED, Number: 2, Content: "some", Position: 2, }, { Mode: diffparser.UNCHANGED, Number: 3, Content: "lines", Position: 3, }, { Mode: diffparser.UNCHANGED, Number: 4, Content: "file1", Position: 5, }, } file := diff.Files[0] origRange := file.Hunks[0].OrigRange newRange := file.Hunks[0].NewRange c.Assert(origRange.Start, gc.Equals, 1) c.Assert(origRange.Length, gc.Equals, 4) c.Assert(newRange.Start, gc.Equals, 1) c.Assert(newRange.Length, gc.Equals, 4) for i, line := range expectedOrigLines { c.Assert(*origRange.Lines[i], gc.DeepEquals, line) } for i, line := range expectedNewLines { c.Assert(*newRange.Lines[i], gc.DeepEquals, line) } }