| // Copyright 2015 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package main |
| |
| import ( |
| "encoding/json" |
| "encoding/xml" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "reflect" |
| "testing" |
| |
| "v.io/jiri/gitutil" |
| "v.io/jiri/jiritest" |
| "v.io/jiri/project" |
| "v.io/x/devtools/internal/test" |
| "v.io/x/devtools/internal/xunit" |
| ) |
| |
| func TestParseCLs(t *testing.T) { |
| type testCase struct { |
| refs string |
| projects string |
| expectErr bool |
| expectedCLs []cl |
| } |
| testCases := []testCase{ |
| // Single ref and project. |
| testCase{ |
| refs: "refs/changes/10/1000/1", |
| projects: "release.go.core", |
| expectErr: false, |
| expectedCLs: []cl{ |
| cl{ |
| clNumber: 1000, |
| patchset: 1, |
| ref: "refs/changes/10/1000/1", |
| project: "release.go.core", |
| }, |
| }, |
| }, |
| |
| // Multiple refs and projects. |
| testCase{ |
| refs: "refs/changes/10/1000/1:refs/changes/20/1020/1", |
| projects: "release.go.core:release.js.core", |
| expectErr: false, |
| expectedCLs: []cl{ |
| cl{ |
| clNumber: 1000, |
| patchset: 1, |
| ref: "refs/changes/10/1000/1", |
| project: "release.go.core", |
| }, |
| cl{ |
| clNumber: 1020, |
| patchset: 1, |
| ref: "refs/changes/20/1020/1", |
| project: "release.js.core", |
| }, |
| }, |
| }, |
| |
| // len(refs) != len(project) |
| testCase{ |
| refs: "refs/changes/10/1000/1:refs/changes/20/1020/1", |
| projects: "release.go.core", |
| expectErr: true, |
| }, |
| } |
| |
| for _, test := range testCases { |
| reviewTargetRefsFlag = test.refs |
| projectsFlag = test.projects |
| gotCLs, err := parseCLs() |
| if test.expectErr && err == nil { |
| t.Fatalf("want errors, got no errors") |
| |
| } |
| if !test.expectErr && err != nil { |
| t.Fatalf("want no errors, got: %v", err) |
| } |
| if err == nil { |
| if !reflect.DeepEqual(test.expectedCLs, gotCLs) { |
| t.Fatalf("want %#v, got %#v", test.expectedCLs, gotCLs) |
| } |
| } |
| } |
| } |
| |
| // TestPresubmitTest is an end-to-end test for the "test" phase of presubmit. |
| // It follows the steps below: |
| // |
| // 1. Create a fake JIRI_ROOT. |
| // 2. Create two CLs in two different projects. Each CL contains a new file with |
| // some specific content. Push those CLs to the remote of the fake JIRI_ROOT. |
| // 3. Run the "test-presubmit-test" presubmit test againest those two CLs. The |
| // "test-presubmit-test" is set up in jiri-test/internal/test/presubmit.go. |
| // It will read the files in the CLs and check their contents. The file names |
| // and the expected contents are passed to presubmit through a set of global |
| // vars. |
| // 4. After the test is done, we verify the test status file and the test report |
| // file. |
| // 5. We run through step 2-4 twice, one for a passed presubmit test, and one |
| // for a failed presubmit test. |
| func TestPresubmitTest(t *testing.T) { |
| fake, _ := jiritest.NewFakeJiriRoot(t) |
| |
| // Set WORKSPACE to JIRI_ROOT. |
| // The test status file and the report file will be written to WORKSPACE. |
| if err := os.Setenv("WORKSPACE", fake.X.Root); err != nil { |
| t.Fatalf("os.Setenv() failed: %v", err) |
| } |
| |
| // Prepare two CLs in two different projects, and push them to remote. |
| project1, branch1, file1, content1 := "p1", "refs/changes/45/12345/1", "f1", "content1" |
| project2, branch2, file2, content2 := "p2", "refs/changes/90/67890/1", "f2", "content2" |
| if err := createCLInProject(fake, project1, file1, content1, branch1); err != nil { |
| t.Fatalf("%v", err) |
| } |
| if err := createCLInProject(fake, project2, file2, content2, branch2); err != nil { |
| t.Fatalf("%v", err) |
| } |
| |
| // Run presubmit for those two CLs with the correct contents. |
| // We expect the presubmit test will pass. |
| projectsFlag = fmt.Sprintf("%s:%s", project1, project2) |
| reviewTargetRefsFlag = fmt.Sprintf("%s:%s", branch1, branch2) |
| testFlag = "test-presubmit-test" |
| testMode = true |
| testFilePaths = fmt.Sprintf("%s:%s", |
| filepath.Join(fake.X.Root, project1, file1), |
| filepath.Join(fake.X.Root, project2, file2)) |
| testFileExpectedContents = fmt.Sprintf("%s:%s", content1, content2) |
| if err := runTest(fake.X, []string{}); err != nil { |
| t.Fatalf("%v", err) |
| } |
| |
| // Make sure we got a test status file with "passed" as the result. |
| statusFile := filepath.Join(fake.X.Root, "status_test_presubmit_test.json") |
| if err := checkStatusFile(statusFile, test.Passed); err != nil { |
| t.Fatalf("%v", err) |
| } |
| |
| // Make sure we got a test report file without failed test cases. |
| reportFile := filepath.Join(fake.X.Root, "tests_dummy.xml") |
| if err := checkReportfile(reportFile, 0); err != nil { |
| t.Fatalf("%v", err) |
| } |
| |
| // Run presubmit again with the incorrect contents. |
| // We expect the presubmit test will fail. |
| os.RemoveAll(statusFile) |
| os.RemoveAll(reportFile) |
| testFileExpectedContents = "c1:c2" |
| if err := runTest(fake.X, []string{}); err != nil { |
| t.Fatalf("%v", err) |
| } |
| |
| // Make sure we got a test status file with "failed" as the result. |
| if err := checkStatusFile(statusFile, test.Failed); err != nil { |
| t.Fatalf("%v", err) |
| } |
| // Make sure we got a test report file with 1 fail test cases. |
| reportFile = filepath.Join(fake.X.Root, "tests_test_presubmit_test.xml") |
| if err := checkReportfile(reportFile, 1); err != nil { |
| t.Fatalf("%v", err) |
| } |
| } |
| |
| func createCLInProject(fake *jiritest.FakeJiriRoot, projectName, fileName, fileContent, branch string) error { |
| // Create the remote project and add it to the manifest. |
| if err := fake.CreateRemoteProject(projectName); err != nil { |
| return err |
| } |
| if err := fake.AddProject(project.Project{ |
| Name: projectName, |
| Path: projectName, |
| Remote: fake.Projects[projectName], |
| }); err != nil { |
| return err |
| } |
| if err := fake.UpdateUniverse(false); err != nil { |
| return err |
| } |
| |
| // Create a CL locally with a new file, and push it to the given branch |
| // in remote. |
| projectPath := filepath.Join(fake.X.Root, projectName) |
| rootDirOpt := gitutil.RootDirOpt(projectPath) |
| s := fake.X.NewSeq() |
| g := gitutil.New(s, rootDirOpt) |
| if err := g.CreateAndCheckoutBranch(branch); err != nil { |
| return err |
| } |
| testFile := filepath.Join(projectPath, fileName) |
| if err := ioutil.WriteFile(testFile, []byte(fileContent), 0644); err != nil { |
| return err |
| } |
| if err := g.Add(testFile); err != nil { |
| return err |
| } |
| if err := g.CommitWithMessage("check in a new file"); err != nil { |
| return err |
| } |
| if err := g.Push("origin", branch, gitutil.VerifyOpt(false)); err != nil { |
| return err |
| } |
| if err := g.CheckoutBranch("master"); err != nil { |
| return err |
| } |
| if err := g.DeleteBranch(branch, gitutil.ForceOpt(true)); err != nil { |
| return err |
| } |
| |
| return nil |
| } |
| |
| func checkStatusFile(statusFile string, expectedStatus test.Status) error { |
| bytes, err := ioutil.ReadFile(statusFile) |
| if err != nil { |
| return fmt.Errorf("ReadFile(%v) failed: %v", statusFile, err) |
| } |
| var result testResultInfo |
| if err := json.Unmarshal(bytes, &result); err != nil { |
| return fmt.Errorf("Unmarshal() failed: %v", err) |
| } |
| if result.Result.Status != expectedStatus { |
| return fmt.Errorf("test status: want %s, got %s", expectedStatus, result.Result.Status) |
| } |
| return nil |
| } |
| |
| func checkReportfile(reportFile string, expectedFailures int) error { |
| bytes, err := ioutil.ReadFile(reportFile) |
| if err != nil { |
| return fmt.Errorf("ReadFile(%v) failed: %v", reportFile, err) |
| } |
| suites := xunit.TestSuites{} |
| if err := xml.Unmarshal(bytes, &suites); err != nil { |
| return fmt.Errorf("Unmarshal(%v) failed: %v", string(bytes), err) |
| } |
| failures := 0 |
| for _, suite := range suites.Suites { |
| failures += suite.Failures |
| } |
| if failures != expectedFailures { |
| return fmt.Errorf("failure count: want %v, got %v", expectedFailures, failures) |
| } |
| return nil |
| } |