| package dkms | |
| import ( | |
| "context" | |
| "testing" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/fakes" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/gcs" | |
| ) | |
| type Action int | |
| const ( | |
| add Action = iota | |
| build | |
| install | |
| remove | |
| unbuild | |
| uninstall | |
| ) | |
| // This is used by TestTransitions and TestCachedTransitions to ensure that | |
| // DKMS state transitions behave as expected. | |
| type testTransition struct { | |
| action Action | |
| expectedStatus PackageStatus | |
| } | |
| var baseTransitions = []testTransition{ | |
| // tests +1 transition cases | |
| {add, Added}, | |
| {build, Built}, | |
| {install, Installed}, | |
| {remove, Broken}, | |
| // tests +2 transition cases | |
| {build, Built}, | |
| {remove, Broken}, | |
| {add, Added}, | |
| {install, Installed}, | |
| {remove, Broken}, | |
| // tests +3 transition cases | |
| {install, Installed}, | |
| {remove, Broken}, | |
| // tests -1 transition cases | |
| {install, Installed}, | |
| {uninstall, Built}, | |
| {unbuild, Added}, | |
| {remove, Broken}, | |
| // tests -2 transition cases | |
| {install, Installed}, | |
| {unbuild, Added}, | |
| {install, Installed}, | |
| {uninstall, Built}, | |
| {remove, Broken}, | |
| // tests -3 transition cases | |
| {install, Installed}, | |
| {remove, Broken}, | |
| } | |
| // test cases where the state is not affected because the target | |
| // state is already reached/passed | |
| var noOpTransitions = []testTransition{ | |
| {remove, Broken}, | |
| {unbuild, Broken}, | |
| {uninstall, Broken}, | |
| {add, Added}, | |
| {add, Added}, | |
| {unbuild, Added}, | |
| {uninstall, Added}, | |
| {build, Built}, | |
| {add, Built}, | |
| {build, Built}, | |
| {uninstall, Built}, | |
| {install, Installed}, | |
| {add, Installed}, | |
| {build, Installed}, | |
| {install, Installed}, | |
| } | |
| // test cases that are no-ops even when options.Force is true | |
| var noOpTransitionsForce = []testTransition{ | |
| {remove, Broken}, | |
| {unbuild, Broken}, | |
| {uninstall, Broken}, | |
| {add, Added}, | |
| {add, Added}, | |
| {unbuild, Added}, | |
| {uninstall, Added}, | |
| {build, Built}, | |
| {build, Built}, | |
| {uninstall, Built}, | |
| {install, Installed}, | |
| {install, Installed}, | |
| } | |
| // these test cases are no-ops by default because they would transition from | |
| // a higher state to a lower state, but with options.Force, the state is | |
| // lowered before performing the action | |
| var loweringTransitionsForce = []testTransition{ | |
| {build, Built}, | |
| {add, Added}, // forced Add will remove/unbuild/uninstall, then add | |
| {install, Installed}, | |
| {add, Added}, // forced Add will remove/unbuild/uninstall, then add | |
| {install, Installed}, | |
| {build, Built}, // forced Build will unbuild/uninstall, then build | |
| } | |
| // These test cases are no-ops by default because they would transition from | |
| // a higher state to a lower state, but with options.Force, the state is | |
| // lowered before performing the action. In all of these cases, the cached | |
| // state will be built because we don't remove modules from the cache. | |
| var loweringTransitionsForceCached = []testTransition{ | |
| {build, Built}, | |
| {add, Built}, // forced Add will remove/unbuild/uninstall, then add | |
| {install, Installed}, | |
| {add, Built}, // forced Add will remove/unbuild/uninstall, then add | |
| {install, Installed}, | |
| {build, Built}, // forced Build will unbuild/uninstall, then build | |
| } | |
| var transitions = append(baseTransitions, noOpTransitions...) | |
| var transitionsForce = append(append(baseTransitions, noOpTransitionsForce...), loweringTransitionsForce...) | |
| var transitionsCachedForce = append(append(baseTransitions, noOpTransitionsForce...), loweringTransitionsForceCached...) | |
| func TestTransitions(t *testing.T) { | |
| options := &Options{ | |
| Force: false, | |
| MakeVariables: "", | |
| // the fake testing module does not have a properly formatted version, | |
| // so we skip the version check | |
| ForceVersionOverride: true, | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-sources", | |
| Install: t.TempDir(), | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| for _, transition := range transitions { | |
| t.Logf("testing transition %v", transition) | |
| var err error | |
| switch transition.action { | |
| case add: | |
| err = Add(pkg, options) | |
| case build: | |
| err = Build(pkg, options) | |
| case install: | |
| err = Install(pkg, options) | |
| case remove: | |
| err = Remove(pkg) | |
| case unbuild: | |
| err = Unbuild(pkg) | |
| case uninstall: | |
| err = Uninstall(pkg) | |
| } | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| status := Status(pkg) | |
| if status != transition.expectedStatus { | |
| t.Fatalf("status was expected to be %v; got %v instead", transition.expectedStatus, status) | |
| } | |
| } | |
| } | |
| func TestTransitionsForce(t *testing.T) { | |
| options := &Options{ | |
| // setting Force should also skip the version check, so we don't need | |
| // to pass ForceVersionOverride | |
| Force: true, | |
| MakeVariables: "", | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-sources", | |
| Install: t.TempDir(), | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| for _, transition := range transitionsForce { | |
| t.Logf("testing transition %v", transition) | |
| var err error | |
| switch transition.action { | |
| case add: | |
| err = Add(pkg, options) | |
| case build: | |
| err = Build(pkg, options) | |
| case install: | |
| err = Install(pkg, options) | |
| case remove: | |
| err = Remove(pkg) | |
| case unbuild: | |
| err = Unbuild(pkg) | |
| case uninstall: | |
| err = Uninstall(pkg) | |
| } | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| status := Status(pkg) | |
| if status != transition.expectedStatus { | |
| t.Fatalf("status was expected to be %v; got %v instead", transition.expectedStatus, status) | |
| } | |
| } | |
| } | |
| func TestCachedTransitions(t *testing.T) { | |
| options := &Options{ | |
| Force: false, | |
| Upload: true, | |
| MakeVariables: "", | |
| // the fake testing module does not have a properly formatted version, | |
| // so we skip the version check | |
| ForceVersionOverride: true, | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-sources", | |
| Install: t.TempDir(), | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| ctx := context.Background() | |
| fakeGCS := fakes.GCSForTest(t) | |
| fakeGCS.LogMissing = false | |
| bucket := gcs.NewGCSBucket(fakeGCS.Client, "test", "dkms") | |
| for _, transition := range transitions { | |
| t.Logf("testing [ %d, %s ]", transition.action, transition.expectedStatus) | |
| var err error | |
| switch transition.action { | |
| case add: | |
| err = CachedAdd(ctx, pkg, bucket, options) | |
| case build: | |
| err = CachedBuild(ctx, pkg, bucket, options) | |
| case install: | |
| err = CachedInstall(ctx, pkg, bucket, options) | |
| case remove: | |
| err = CachedRemove(ctx, pkg, bucket, options) | |
| case unbuild: | |
| err = CachedUnbuild(ctx, pkg, bucket, options) | |
| case uninstall: | |
| err = Uninstall(pkg) | |
| } | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| status := CachedStatus(ctx, pkg, bucket) | |
| if status != transition.expectedStatus { | |
| t.Fatalf("status was expected to be %v; got %v instead", transition.expectedStatus, status) | |
| } | |
| } | |
| } | |
| func TestCachedTransitionsForce(t *testing.T) { | |
| options := &Options{ | |
| Force: true, | |
| Upload: true, | |
| MakeVariables: "", | |
| // the fake testing module does not have a properly formatted version, | |
| // so we skip the version check | |
| ForceVersionOverride: true, | |
| } | |
| trees := &Trees{ | |
| Dkms: t.TempDir(), | |
| Source: "testdata/source-tree", | |
| Kernel: "testdata/kernel-sources", | |
| Install: t.TempDir(), | |
| } | |
| pkg := &Package{ | |
| Name: "mymodule", | |
| Version: "1.0", | |
| KernelVersion: "6.1.100", | |
| Arch: "x86_64", | |
| BuildId: "18244.151.14", | |
| Trees: trees, | |
| } | |
| ctx := context.Background() | |
| fakeGCS := fakes.GCSForTest(t) | |
| fakeGCS.LogMissing = false | |
| bucket := gcs.NewGCSBucket(fakeGCS.Client, "test", "dkms") | |
| // for _, transition := range transitionsForce { | |
| for _, transition := range transitionsCachedForce { | |
| t.Logf("testing transition %v", transition) | |
| var err error | |
| switch transition.action { | |
| case add: | |
| err = CachedAdd(ctx, pkg, bucket, options) | |
| case build: | |
| err = CachedBuild(ctx, pkg, bucket, options) | |
| case install: | |
| err = CachedInstall(ctx, pkg, bucket, options) | |
| case remove: | |
| err = CachedRemove(ctx, pkg, bucket, options) | |
| case unbuild: | |
| err = CachedUnbuild(ctx, pkg, bucket, options) | |
| case uninstall: | |
| err = Uninstall(pkg) | |
| } | |
| if err != nil { | |
| t.Fatalf("%v", err) | |
| } | |
| status := CachedStatus(ctx, pkg, bucket) | |
| if status != transition.expectedStatus { | |
| t.Fatalf("status was expected to be %v; got %v instead", transition.expectedStatus, status) | |
| } | |
| } | |
| } |