From 9f3e65cc15503be16e953505d952bc2ba84795ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Irving=20Mondrag=C3=B3n?= Date: Thu, 19 Feb 2026 20:50:56 +0100 Subject: [PATCH] test(e2e): migrate nerdctl tests to Ginkgo framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Irving Mondragón --- Makefile | 8 +-- tests/e2e/ctr_test.go | 15 +---- tests/e2e/e2e_test.go | 10 --- tests/e2e/nerdctl_test.go | 128 ++++++++++++++++++++++++++++++++++++++ tests/e2e/setup_test.go | 9 +-- tests/e2e/suite_test.go | 33 +++++++++- 6 files changed, 168 insertions(+), 35 deletions(-) create mode 100644 tests/e2e/nerdctl_test.go diff --git a/Makefile b/Makefile index 4c8cc07e..f8d8f627 100644 --- a/Makefile +++ b/Makefile @@ -265,14 +265,14 @@ test_hypervisors: .PHONY: test_nerdctl test_nerdctl: @echo "Testing nerdctl" - @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -run TestNerdctl -v + @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -run TestE2E -v --ginkgo.v --ginkgo.focus="Nerdctl" @echo " " ## test_ctr Run all end-to-end tests with ctr .PHONY: test_ctr test_ctr: @echo "Testing ctr" - @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -run TestCtr -v --ginkgo.v + @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -run TestE2E -v --ginkgo.v --ginkgo.focus="Ctr" @echo " " ## test_crictl Run all end-to-end tests with crictl @@ -293,14 +293,14 @@ test_docker: .PHONY: test_nerdctl_% test_nerdctl_%: @echo "Testing nerdctl" - @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -v -run "TestNerdctl/$*" + @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -v --ginkgo.v -run TestE2E --ginkgo.focus="Nerdctl.*$*" @echo " " ## test_ctr_[pattern] Run all end-to-end tests with ctr that match pattern .PHONY: test_ctr_% test_ctr_%: @echo "Testing ctr" - @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -v --ginkgo.v -run TestCtr --ginkgo.focus="$*" + @GOFLAGS=$(TEST_FLAGS) $(GO) test $(TEST_OPTS) ./tests/e2e -v --ginkgo.v -run TestE2E --ginkgo.focus="Ctr.*$*" @echo " " ## test_crictl_[pattern] Run all end-to-end tests with crictl that match pattern diff --git a/tests/e2e/ctr_test.go b/tests/e2e/ctr_test.go index 163598f8..4548ea27 100644 --- a/tests/e2e/ctr_test.go +++ b/tests/e2e/ctr_test.go @@ -25,10 +25,7 @@ import ( const testCtr = "TestCtr" var _ = Describe("Ctr", Ordered, ContinueOnFailure, func() { - var ( - tool *ctrInfo - cwd string - ) + var tool *ctrInfo BeforeAll(func() { cases := ctrTestCases() @@ -42,15 +39,7 @@ var _ = Describe("Ctr", Ordered, ContinueOnFailure, func() { }) BeforeEach(func() { - var err error - cwd, err = os.Getwd() - Expect(err).NotTo(HaveOccurred()) - testDir := GinkgoT().TempDir() - Expect(os.Chdir(testDir)).To(Succeed()) - - DeferCleanup(func() { - Expect(os.Chdir(cwd)).To(Succeed()) - }) + setupTestDir() }) ReportAfterEach(func(report SpecReport) { diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 3c918cac..27f33114 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -18,16 +18,6 @@ import ( "testing" ) -func TestNerdctl(t *testing.T) { - tests := nerdctlTestCases() - for _, tc := range tests { - t.Run(tc.Name, func(t *testing.T) { - nerdctlTool := newNerdctlTool(tc) - runTest(nerdctlTool, t) - }) - } -} - func TestCrictl(t *testing.T) { tests := crictlTestCases() for _, tc := range tests { diff --git a/tests/e2e/nerdctl_test.go b/tests/e2e/nerdctl_test.go new file mode 100644 index 00000000..4871ce21 --- /dev/null +++ b/tests/e2e/nerdctl_test.go @@ -0,0 +1,128 @@ +// Copyright (c) 2023-2026, Nubificus LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package urunce2etesting + +import ( + "fmt" + "os" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +const testNerdctl = "TestNerdctl" + +var _ = Describe("Nerdctl", Ordered, ContinueOnFailure, func() { + var tool *nerdctlInfo + + BeforeAll(func() { + cases := nerdctlTestCases() + images := getTestImages(cases) + err := pullAllImages(testNerdctl, images) + Expect(err).NotTo(HaveOccurred(), "Failed to pull nerdctl images") + + DeferCleanup(func() { + removeAllImages(testNerdctl, images) + }) + }) + + BeforeEach(func() { + setupTestDir() + }) + + ReportAfterEach(func(report SpecReport) { + if report.Failed() && tool != nil { + AddReportEntry("test-args", tool.getTestArgs()) + } + }) + + Context("foreground containers", func() { + DescribeTable("unikernel containers", + func(tc containerTestArgs) { + for _, vol := range tc.Volumes { + if _, err := os.Stat(vol.Source); err != nil { + Skip(fmt.Sprintf("Could not find %s", vol.Source)) + } + } + + tool = newNerdctlTool(tc) + tool.setContainerID(tc.Name) + + DeferCleanup(func() { + if tool != nil && tool.getContainerID() != "" { + By("Cleaning up container") + if err := testCleanup(tool); err != nil { + GinkgoLogr.Error(err, "Container cleanup failed") + } + } + }) + + By("Running container") + output, err := tool.runContainer(false) + Expect(err).NotTo(HaveOccurred(), "Failed to run container: %s", output) + + By("Verifying container output") + Expect(output).To(ContainSubstring(tc.ExpectOut)) + }, + toTableEntries(selectTestCases(nerdctlTestCases(), false)), + ) + }) + + Context("detached containers", func() { + DescribeTable("unikernel containers", + func(tc containerTestArgs) { + for _, vol := range tc.Volumes { + if _, err := os.Stat(vol.Source); err != nil { + Skip(fmt.Sprintf("Could not find %s", vol.Source)) + } + } + + tool = newNerdctlTool(tc) + + By("Creating container") + cID, err := tool.createContainer() + Expect(err).NotTo(HaveOccurred(), "Failed to create container: %s", cID) + tool.setContainerID(cID) + + DeferCleanup(func() { + if tool != nil && tool.getContainerID() != "" { + By("Stopping container") + if err := tool.stopContainer(); err != nil { + GinkgoLogr.Error(err, "Failed to stop container") + } + By("Removing container") + if err := tool.rmContainer(); err != nil { + GinkgoLogr.Error(err, "Failed to remove container") + } + By("Verifying container removal") + if err := testVerifyRm(tool); err != nil { + GinkgoLogr.Error(err, "Failed to verify removal") + } + } + }) + + By("Starting container") + output, err := tool.startContainer(true) + Expect(err).NotTo(HaveOccurred(), "Failed to start container: %s", output) + + By("Running test function") + Eventually(func() error { + return tc.TestFunc(tool) + }, defaultTimeout, defaultInterval).Should(Succeed()) + }, + toTableEntries(selectTestCases(nerdctlTestCases(), true)), + ) + }) +}) diff --git a/tests/e2e/setup_test.go b/tests/e2e/setup_test.go index 1c7a747a..bd0cce6f 100644 --- a/tests/e2e/setup_test.go +++ b/tests/e2e/setup_test.go @@ -25,7 +25,7 @@ import ( ) const ( - testNerdctl = "TestNerdctl" + testE2E = "TestE2E" testCrictl = "TestCrictl" testDocker = "TestDocker" maxPullRetries = 5 @@ -68,10 +68,8 @@ func parseTestPattern() (string, string) { func getTestCases(testFunc string) []containerTestArgs { switch testFunc { - case testNerdctl: - return nerdctlTestCases() - case testCtr: - // Images managed by BeforeAll in ctr_test.go + case testE2E: + // Images managed by BeforeAll in Ginkgo specs return []containerTestArgs{} case testCrictl: return crictlTestCases() @@ -79,7 +77,6 @@ func getTestCases(testFunc string) []containerTestArgs { return dockerTestCases() default: var allCases []containerTestArgs - allCases = append(allCases, nerdctlTestCases()...) allCases = append(allCases, crictlTestCases()...) allCases = append(allCases, dockerTestCases()...) return allCases diff --git a/tests/e2e/suite_test.go b/tests/e2e/suite_test.go index 962a97b4..d272cf0c 100644 --- a/tests/e2e/suite_test.go +++ b/tests/e2e/suite_test.go @@ -15,17 +15,35 @@ package urunce2etesting import ( + "os" "testing" + "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/format" ) -func TestCtr(t *testing.T) { +const ( + defaultTimeout = 10 * time.Second + defaultInterval = 1 * time.Second +) + +func TestE2E(t *testing.T) { format.MaxLength = 0 // Do not truncate failure output RegisterFailHandler(Fail) - RunSpecs(t, "Ctr E2E Suite") + RunSpecs(t, "E2E Suite") +} + +// setupTestDir switches to a temp directory, restored via DeferCleanup. +func setupTestDir() { + cwd, err := os.Getwd() + Expect(err).NotTo(HaveOccurred()) + testDir := GinkgoT().TempDir() + Expect(os.Chdir(testDir)).To(Succeed()) + DeferCleanup(func() { + Expect(os.Chdir(cwd)).To(Succeed()) + }) } // toTableEntries converts a slice of containerTestArgs into Ginkgo TableEntry @@ -37,3 +55,14 @@ func toTableEntries(cases []containerTestArgs) []TableEntry { } return entries } + +// selectTestCases returns cases with or without a TestFunc. +func selectTestCases(cases []containerTestArgs, hasTestFunc bool) []containerTestArgs { + var out []containerTestArgs + for _, tc := range cases { + if (tc.TestFunc != nil) == hasTestFunc { + out = append(out, tc) + } + } + return out +}