vendor: Update everything

GitHub-Pull-Request: https://github.com/syncthing/syncthing/pull/4620
This commit is contained in:
Jakob Borg
2017-12-29 11:38:00 +00:00
parent 1296a22069
commit c24bf7ea55
1070 changed files with 294926 additions and 488191 deletions
+4 -4
View File
@@ -43,10 +43,10 @@ func BuildBootstrapCommand() *Command {
var bootstrapText = `package {{.Package}}
import (
"testing"
{{.GinkgoImport}}
{{.GomegaImport}}
"testing"
)
func Test{{.FormattedName}}(t *testing.T) {
@@ -58,11 +58,11 @@ func Test{{.FormattedName}}(t *testing.T) {
var agoutiBootstrapText = `package {{.Package}}
import (
"testing"
{{.GinkgoImport}}
{{.GomegaImport}}
"github.com/sclevine/agouti"
"testing"
)
func Test{{.FormattedName}}(t *testing.T) {
+1 -1
View File
@@ -46,7 +46,7 @@ func (r *SpecBuilder) BuildSpecs(args []string, additionalArgs []string) {
passed := true
for _, suite := range suites {
runner := testrunner.New(suite, 1, false, r.commandFlags.GoOpts, nil)
runner := testrunner.New(suite, 1, false, 0, r.commandFlags.GoOpts, nil)
fmt.Printf("Compiling %s...\n", suite.PackageName)
path, _ := filepath.Abs(filepath.Join(suite.Path, fmt.Sprintf("%s.test", suite.PackageName)))
+2 -1
View File
@@ -3,8 +3,9 @@ package main
import (
"flag"
"fmt"
"github.com/onsi/ginkgo/ginkgo/convert"
"os"
"github.com/onsi/ginkgo/ginkgo/convert"
)
func BuildConvertCommand() *Command {
+6 -6
View File
@@ -34,10 +34,10 @@ func BuildGenerateCommand() *Command {
var specText = `package {{.Package}}
import (
{{if .DotImportPackage}}. "{{.PackageImportPath}}"{{end}}
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
{{if .DotImportPackage}}. "{{.PackageImportPath}}"{{end}}
)
var _ = Describe("{{.Subject}}", func() {
@@ -45,15 +45,15 @@ var _ = Describe("{{.Subject}}", func() {
})
`
var agoutiSpecText = `package {{.Package}}_test
var agoutiSpecText = `package {{.Package}}
import (
{{if .DotImportPackage}}. "{{.PackageImportPath}}"{{end}}
{{if .IncludeImports}}. "github.com/onsi/ginkgo"{{end}}
{{if .IncludeImports}}. "github.com/onsi/gomega"{{end}}
. "github.com/sclevine/agouti/matchers"
"github.com/sclevine/agouti"
. "github.com/sclevine/agouti/matchers"
{{if .DotImportPackage}}. "{{.PackageImportPath}}"{{end}}
)
var _ = Describe("{{.Subject}}", func() {
+4
View File
@@ -50,6 +50,10 @@ By default, when running multiple tests (with -r or a list of packages) Ginkgo w
ginkgo -keepGoing
To fail if there are ginkgo tests in a directory but no test suite (missing `RunSpecs`)
ginkgo -requireSuite
To monitor packages and rerun tests when changes occur:
ginkgo watch <-r> </path/to/package>
+2 -1
View File
@@ -3,11 +3,12 @@ package main
import (
"bufio"
"flag"
"github.com/onsi/ginkgo/ginkgo/nodot"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"github.com/onsi/ginkgo/ginkgo/nodot"
)
func BuildNodotCommand() *Command {
+80 -3
View File
@@ -5,8 +5,12 @@ import (
"fmt"
"math/rand"
"os"
"strings"
"time"
"io/ioutil"
"path/filepath"
"github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/ginkgo/interrupthandler"
"github.com/onsi/ginkgo/ginkgo/testrunner"
@@ -71,7 +75,7 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
runners := []*testrunner.TestRunner{}
for _, suite := range suites {
runners = append(runners, testrunner.New(suite, r.commandFlags.NumCPU, r.commandFlags.ParallelStream, r.commandFlags.GoOpts, additionalArgs))
runners = append(runners, testrunner.New(suite, r.commandFlags.NumCPU, r.commandFlags.ParallelStream, r.commandFlags.Timeout, r.commandFlags.GoOpts, additionalArgs))
}
numSuites := 0
@@ -100,6 +104,18 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
runResult, numSuites = r.suiteRunner.RunSuites(randomizedRunners, r.commandFlags.NumCompilers, r.commandFlags.KeepGoing, nil)
}
if r.isInCoverageMode() {
if r.getOutputDir() != "" {
// If coverprofile is set, combine coverages
if r.getCoverprofile() != "" {
r.combineCoverprofiles(runners)
} else {
// Just move them
r.moveCoverprofiles(runners)
}
}
}
for _, runner := range runners {
runner.CleanUp()
}
@@ -107,7 +123,7 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
fmt.Printf("\nGinkgo ran %d %s in %s\n", numSuites, pluralizedWord("suite", "suites", numSuites), time.Since(t))
if runResult.Passed {
if runResult.HasProgrammaticFocus {
if runResult.HasProgrammaticFocus && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
fmt.Printf("Test Suite Passed\n")
fmt.Printf("Detected Programmatic Focus - setting exit status to %d\n", types.GINKGO_FOCUS_EXIT_CODE)
os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
@@ -121,6 +137,67 @@ func (r *SpecRunner) RunSpecs(args []string, additionalArgs []string) {
}
}
// Moves all generated profiles to specified directory
func (r *SpecRunner) moveCoverprofiles(runners []*testrunner.TestRunner) {
for _, runner := range runners {
_, filename := filepath.Split(runner.CoverageFile)
err := os.Rename(runner.CoverageFile, filepath.Join(r.getOutputDir(), filename))
if err != nil {
fmt.Printf("Unable to move coverprofile %s, %v\n", runner.CoverageFile, err)
return
}
}
}
// Combines all generated profiles in the specified directory
func (r *SpecRunner) combineCoverprofiles(runners []*testrunner.TestRunner) {
path, _ := filepath.Abs(r.getOutputDir())
fmt.Println("path is " + path)
os.MkdirAll(path, os.ModePerm)
combined, err := os.OpenFile(filepath.Join(path, r.getCoverprofile()),
os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Printf("Unable to create combined profile, %v\n", err)
return
}
for _, runner := range runners {
contents, err := ioutil.ReadFile(runner.CoverageFile)
if err != nil {
fmt.Printf("Unable to read coverage file %s to combine, %v\n", runner.CoverageFile, err)
return
}
_, err = combined.Write(contents)
if err != nil {
fmt.Printf("Unable to append to coverprofile, %v\n", err)
return
}
}
fmt.Println("All profiles combined")
}
func (r *SpecRunner) isInCoverageMode() bool {
opts := r.commandFlags.GoOpts
return *opts["cover"].(*bool) || *opts["coverpkg"].(*string) != "" || *opts["covermode"].(*string) != ""
}
func (r *SpecRunner) getCoverprofile() string {
return *r.commandFlags.GoOpts["coverprofile"].(*string)
}
func (r *SpecRunner) getOutputDir() string {
return *r.commandFlags.GoOpts["outputdir"].(*string)
}
func (r *SpecRunner) ComputeSuccinctMode(numSuites int) {
if config.DefaultReporterConfig.Verbose {
config.DefaultReporterConfig.Succinct = false
@@ -171,7 +248,7 @@ func orcMessage(iteration int) string {
"Still good...",
"I think your tests are fine....",
"Yep, still passing",
"Here we go again...",
"Oh boy, here I go testin' again!",
"Even the gophers are getting bored",
"Did you try -race?",
"Maybe you should stop now?",
+8 -1
View File
@@ -4,6 +4,8 @@ import (
"flag"
"runtime"
"time"
"github.com/onsi/ginkgo/config"
)
@@ -19,6 +21,7 @@ type RunWatchAndBuildCommandFlags struct {
Notify bool
AfterSuiteHook string
AutoNodes bool
Timeout time.Duration
//only for run command
KeepGoing bool
@@ -26,7 +29,8 @@ type RunWatchAndBuildCommandFlags struct {
RandomizeSuites bool
//only for watch command
Depth int
Depth int
WatchRegExp string
FlagSet *flag.FlagSet
}
@@ -135,6 +139,7 @@ func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
c.FlagSet.StringVar(c.stringSlot("memprofile"), "memprofile", "", "Write a memory profile to the specified file after all tests have passed.")
c.FlagSet.IntVar(c.intSlot("memprofilerate"), "memprofilerate", 0, "Enable more precise (and expensive) memory profiles by setting runtime.MemProfileRate.")
c.FlagSet.StringVar(c.stringSlot("outputdir"), "outputdir", "", "Place output files from profiling in the specified directory.")
c.FlagSet.BoolVar(c.boolSlot("requireSuite"), "requireSuite", false, "Fail if there are ginkgo tests in a directory but no test suite (missing RunSpecs)")
if mode == runMode || mode == watchMode {
config.Flags(c.FlagSet, "", false)
@@ -146,6 +151,7 @@ func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
c.FlagSet.BoolVar(&(c.Notify), "notify", false, "Send desktop notifications when a test run completes")
}
c.FlagSet.StringVar(&(c.AfterSuiteHook), "afterSuiteHook", "", "Run a command when a suite test run completes")
c.FlagSet.DurationVar(&(c.Timeout), "timeout", 24*time.Hour, "Suite fails if it does not complete within the specified timeout")
}
if mode == runMode {
@@ -156,5 +162,6 @@ func (c *RunWatchAndBuildCommandFlags) flags(mode int) {
if mode == watchMode {
c.FlagSet.IntVar(&(c.Depth), "depth", 1, "Ginkgo will watch dependencies down to this depth in the dependency tree")
c.FlagSet.StringVar(&(c.WatchRegExp), "watchRegExp", `\.go$`, "Files matching this regular expression will be watched for changes")
}
}
+98 -50
View File
@@ -8,7 +8,6 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"syscall"
@@ -29,17 +28,23 @@ type TestRunner struct {
numCPU int
parallelStream bool
timeout time.Duration
goOpts map[string]interface{}
additionalArgs []string
stderr *bytes.Buffer
CoverageFile string
}
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, goOpts map[string]interface{}, additionalArgs []string) *TestRunner {
func New(suite testsuite.TestSuite, numCPU int, parallelStream bool, timeout time.Duration, goOpts map[string]interface{}, additionalArgs []string) *TestRunner {
runner := &TestRunner{
Suite: suite,
numCPU: numCPU,
parallelStream: parallelStream,
goOpts: goOpts,
additionalArgs: additionalArgs,
timeout: timeout,
stderr: new(bytes.Buffer),
}
if !suite.Precompiled {
@@ -60,10 +65,10 @@ func (t *TestRunner) Compile() error {
func (t *TestRunner) BuildArgs(path string) []string {
args := []string{"test", "-c", "-i", "-o", path, t.Suite.Path}
if *t.goOpts["covermode"].(*string) != "" {
args = append(args, "-cover", fmt.Sprintf("-covermode=%s", *t.goOpts["covermode"].(*string)))
if t.getCoverMode() != "" {
args = append(args, "-cover", fmt.Sprintf("-covermode=%s", t.getCoverMode()))
} else {
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" {
if t.shouldCover() || t.getCoverPackage() != "" {
args = append(args, "-cover", "-covermode=atomic")
}
}
@@ -136,13 +141,16 @@ func (t *TestRunner) CompileTo(path string) error {
output, err := cmd.CombinedOutput()
if err != nil {
fixedOutput := fixCompilationOutput(string(output), t.Suite.Path)
if len(output) > 0 {
return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, fixedOutput)
return fmt.Errorf("Failed to compile %s:\n\n%s", t.Suite.PackageName, output)
}
return fmt.Errorf("Failed to compile %s", t.Suite.PackageName)
}
if len(output) > 0 {
fmt.Println(string(output))
}
if fileExists(path) == false {
compiledFile := t.Suite.PackageName + ".test"
if fileExists(compiledFile) {
@@ -215,38 +223,6 @@ func copyFile(src, dst string) error {
return out.Chmod(mode)
}
/*
go test -c -i spits package.test out into the cwd. there's no way to change this.
to make sure it doesn't generate conflicting .test files in the cwd, Compile() must switch the cwd to the test package.
unfortunately, this causes go test's compile output to be expressed *relative to the test package* instead of the cwd.
this makes it hard to reason about what failed, and also prevents iterm's Cmd+click from working.
fixCompilationOutput..... rewrites the output to fix the paths.
yeah......
*/
func fixCompilationOutput(output string, relToPath string) string {
relToPath = filepath.Join(relToPath)
re := regexp.MustCompile(`^(\S.*\.go)\:\d+\:`)
lines := strings.Split(output, "\n")
for i, line := range lines {
indices := re.FindStringSubmatchIndex(line)
if len(indices) == 0 {
continue
}
path := line[indices[2]:indices[3]]
if filepath.Dir(path) != relToPath {
path = filepath.Join(relToPath, path)
lines[i] = path + line[indices[3]:]
}
}
return strings.Join(lines, "\n")
}
func (t *TestRunner) Run() RunResult {
if t.Suite.IsGinkgo {
if t.numCPU > 1 {
@@ -324,7 +300,7 @@ func (t *TestRunner) runAndStreamParallelGinkgoSuite() RunResult {
os.Stdout.Sync()
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
if t.shouldCombineCoverprofiles() {
t.combineCoverprofiles()
}
@@ -406,21 +382,40 @@ func (t *TestRunner) runParallelGinkgoSuite() RunResult {
os.Stdout.Sync()
}
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
if t.shouldCombineCoverprofiles() {
t.combineCoverprofiles()
}
return res
}
const CoverProfileSuffix = ".coverprofile"
func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.Cmd {
args := []string{"--test.timeout=24h"}
if *t.goOpts["cover"].(*bool) || *t.goOpts["coverpkg"].(*string) != "" || *t.goOpts["covermode"].(*string) != "" {
coverprofile := "--test.coverprofile=" + t.Suite.PackageName + ".coverprofile"
if t.numCPU > 1 {
coverprofile = fmt.Sprintf("%s.%d", coverprofile, node)
args := []string{"--test.timeout=" + t.timeout.String()}
coverProfile := t.getCoverProfile()
if t.shouldCombineCoverprofiles() {
testCoverProfile := "--test.coverprofile="
coverageFile := ""
// Set default name for coverage results
if coverProfile == "" {
coverageFile = t.Suite.PackageName + CoverProfileSuffix
} else {
coverageFile = coverProfile
}
args = append(args, coverprofile)
testCoverProfile += coverageFile
t.CoverageFile = filepath.Join(t.Suite.Path, coverageFile)
if t.numCPU > 1 {
testCoverProfile = fmt.Sprintf("%s.%d", testCoverProfile, node)
}
args = append(args, testCoverProfile)
}
args = append(args, ginkgoArgs...)
@@ -434,12 +429,36 @@ func (t *TestRunner) cmd(ginkgoArgs []string, stream io.Writer, node int) *exec.
cmd := exec.Command(path, args...)
cmd.Dir = t.Suite.Path
cmd.Stderr = stream
cmd.Stderr = io.MultiWriter(stream, t.stderr)
cmd.Stdout = stream
return cmd
}
func (t *TestRunner) shouldCover() bool {
return *t.goOpts["cover"].(*bool)
}
func (t *TestRunner) shouldRequireSuite() bool {
return *t.goOpts["requireSuite"].(*bool)
}
func (t *TestRunner) getCoverProfile() string {
return *t.goOpts["coverprofile"].(*string)
}
func (t *TestRunner) getCoverPackage() string {
return *t.goOpts["coverpkg"].(*string)
}
func (t *TestRunner) getCoverMode() string {
return *t.goOpts["covermode"].(*string)
}
func (t *TestRunner) shouldCombineCoverprofiles() bool {
return t.shouldCover() || t.getCoverPackage() != "" || t.getCoverMode() != ""
}
func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
var res RunResult
@@ -456,17 +475,34 @@ func (t *TestRunner) run(cmd *exec.Cmd, completions chan RunResult) RunResult {
}
cmd.Wait()
exitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus()
res.Passed = (exitStatus == 0) || (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
res.HasProgrammaticFocus = (exitStatus == types.GINKGO_FOCUS_EXIT_CODE)
if strings.Contains(t.stderr.String(), "warning: no tests to run") {
if t.shouldRequireSuite() {
res.Passed = false
}
fmt.Fprintf(os.Stderr, `Found no test suites, did you forget to run "ginkgo bootstrap"?`)
}
return res
}
func (t *TestRunner) combineCoverprofiles() {
profiles := []string{}
coverProfile := t.getCoverProfile()
for cpu := 1; cpu <= t.numCPU; cpu++ {
coverFile := fmt.Sprintf("%s.coverprofile.%d", t.Suite.PackageName, cpu)
var coverFile string
if coverProfile == "" {
coverFile = fmt.Sprintf("%s%s.%d", t.Suite.PackageName, CoverProfileSuffix, cpu)
} else {
coverFile = fmt.Sprintf("%s.%d", coverProfile, cpu)
}
coverFile = filepath.Join(t.Suite.Path, coverFile)
coverProfile, err := ioutil.ReadFile(coverFile)
os.Remove(coverFile)
@@ -502,5 +538,17 @@ func (t *TestRunner) combineCoverprofiles() {
output = append(output, fmt.Sprintf("%s %d", line, lines[line]))
}
finalOutput := strings.Join(output, "\n")
ioutil.WriteFile(filepath.Join(t.Suite.Path, fmt.Sprintf("%s.coverprofile", t.Suite.PackageName)), []byte(finalOutput), 0666)
finalFilename := ""
if coverProfile != "" {
finalFilename = coverProfile
} else {
finalFilename = fmt.Sprintf("%s%s", t.Suite.PackageName, CoverProfileSuffix)
}
coverageFilepath := filepath.Join(t.Suite.Path, finalFilename)
ioutil.WriteFile(coverageFilepath, []byte(finalOutput), 0666)
t.CoverageFile = coverageFilepath
}
+5 -1
View File
@@ -77,7 +77,11 @@ func relPath(dir string) string {
dir, _ = filepath.Abs(dir)
cwd, _ := os.Getwd()
dir, _ = filepath.Rel(cwd, filepath.Clean(dir))
dir = "." + string(filepath.Separator) + dir
if string(dir[0]) != "." {
dir = "." + string(filepath.Separator) + dir
}
return dir
}
+1
View File
@@ -3,6 +3,7 @@ package main
import (
"flag"
"fmt"
"github.com/onsi/ginkgo/config"
)
+6 -2
View File
@@ -3,6 +3,8 @@ package watch
import (
"fmt"
"regexp"
"github.com/onsi/ginkgo/ginkgo/testsuite"
)
@@ -10,14 +12,16 @@ type SuiteErrors map[testsuite.TestSuite]error
type DeltaTracker struct {
maxDepth int
watchRegExp *regexp.Regexp
suites map[string]*Suite
packageHashes *PackageHashes
}
func NewDeltaTracker(maxDepth int) *DeltaTracker {
func NewDeltaTracker(maxDepth int, watchRegExp *regexp.Regexp) *DeltaTracker {
return &DeltaTracker{
maxDepth: maxDepth,
packageHashes: NewPackageHashes(),
watchRegExp: watchRegExp,
packageHashes: NewPackageHashes(watchRegExp),
suites: map[string]*Suite{},
}
}
+8 -7
View File
@@ -8,7 +8,6 @@ import (
"time"
)
var goRegExp = regexp.MustCompile(`\.go$`)
var goTestRegExp = regexp.MustCompile(`_test\.go$`)
type PackageHash struct {
@@ -16,14 +15,16 @@ type PackageHash struct {
TestModifiedTime time.Time
Deleted bool
path string
codeHash string
testHash string
path string
codeHash string
testHash string
watchRegExp *regexp.Regexp
}
func NewPackageHash(path string) *PackageHash {
func NewPackageHash(path string, watchRegExp *regexp.Regexp) *PackageHash {
p := &PackageHash{
path: path,
path: path,
watchRegExp: watchRegExp,
}
p.codeHash, _, p.testHash, _, p.Deleted = p.computeHashes()
@@ -82,7 +83,7 @@ func (p *PackageHash) computeHashes() (codeHash string, codeModifiedTime time.Ti
continue
}
if goRegExp.Match([]byte(info.Name())) {
if p.watchRegExp.Match([]byte(info.Name())) {
codeHash += p.hashForFileInfo(info)
if info.ModTime().After(codeModifiedTime) {
codeModifiedTime = info.ModTime()
+5 -2
View File
@@ -2,19 +2,22 @@ package watch
import (
"path/filepath"
"regexp"
"sync"
)
type PackageHashes struct {
PackageHashes map[string]*PackageHash
usedPaths map[string]bool
watchRegExp *regexp.Regexp
lock *sync.Mutex
}
func NewPackageHashes() *PackageHashes {
func NewPackageHashes(watchRegExp *regexp.Regexp) *PackageHashes {
return &PackageHashes{
PackageHashes: map[string]*PackageHash{},
usedPaths: nil,
watchRegExp: watchRegExp,
lock: &sync.Mutex{},
}
}
@@ -41,7 +44,7 @@ func (p *PackageHashes) Add(path string) *PackageHash {
path, _ = filepath.Abs(path)
_, ok := p.PackageHashes[path]
if !ok {
p.PackageHashes[path] = NewPackageHash(path)
p.PackageHashes[path] = NewPackageHash(path, p.watchRegExp)
}
if p.usedPaths != nil {
+3 -2
View File
@@ -3,6 +3,7 @@ package main
import (
"flag"
"fmt"
"regexp"
"time"
"github.com/onsi/ginkgo/config"
@@ -58,7 +59,7 @@ func (w *SpecWatcher) runnersForSuites(suites []testsuite.TestSuite, additionalA
runners := []*testrunner.TestRunner{}
for _, suite := range suites {
runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.GoOpts, additionalArgs))
runners = append(runners, testrunner.New(suite, w.commandFlags.NumCPU, w.commandFlags.ParallelStream, w.commandFlags.Timeout, w.commandFlags.GoOpts, additionalArgs))
}
return runners
@@ -72,7 +73,7 @@ func (w *SpecWatcher) WatchSuites(args []string, additionalArgs []string) {
}
fmt.Printf("Identified %d test %s. Locating dependencies to a depth of %d (this may take a while)...\n", len(suites), pluralizedWord("suite", "suites", len(suites)), w.commandFlags.Depth)
deltaTracker := watch.NewDeltaTracker(w.commandFlags.Depth)
deltaTracker := watch.NewDeltaTracker(w.commandFlags.Depth, regexp.MustCompile(w.commandFlags.WatchRegExp))
delta, errors := deltaTracker.Delta(suites)
fmt.Printf("Watching %d %s:\n", len(delta.NewSuites), pluralizedWord("suite", "suites", len(delta.NewSuites)))