示例#1
0
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)
	}
}
示例#2
0
文件: review.go 项目: axw/lingo
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)
	}
}
示例#3
0
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)
	}
}