| new file mode 100644 |
| index 000000000..595156b38 |
| --- /dev/null |
| +++ b/internal/cgroup/sizes_test.go |
| @@ -0,0 +1,101 @@ |
| +package cgroup |
| + |
| +import ( |
| + "go/ast" |
| + "go/parser" |
| + "go/token" |
| + "reflect" |
| + "testing" |
| +) |
| + |
| +func TestSizesTable(t *testing.T) { |
| + want := []Size{ |
| + {Name: "small", Limit: Limit{MemoryMax: 256 << 20}, Shares: Shares{CPUWeight: 50, CPUQuotaPercent: 50, IOWeight: 50}}, |
| + {Name: "standard", Limit: Limit{MemoryMax: 512 << 20}, Shares: Shares{CPUWeight: 100, CPUQuotaPercent: 100, IOWeight: 100}}, |
| + {Name: "large", Limit: Limit{MemoryMax: 1 << 30}, Shares: Shares{CPUWeight: 100, CPUQuotaPercent: 200, IOWeight: 100}}, |
| + {Name: "xlarge", Limit: Limit{MemoryMax: 2 << 30}, Shares: Shares{CPUWeight: 200, CPUQuotaPercent: 400, IOWeight: 200}}, |
| + } |
| + if !reflect.DeepEqual(Sizes, want) { |
| + t.Fatalf("Sizes = %+v\nwant %+v", Sizes, want) |
| + } |
| + |
| + // standard and large are today's tiers, number for number. The test |
| + // holds them equal, not the initializer (the rows are literals). |
| + if Sizes[1].Limit != TierDefault || Sizes[1].Shares != SharesOf(TierDefault) { |
| + t.Errorf("standard = %+v, want TierDefault %+v with shares %+v", Sizes[1], TierDefault, SharesOf(TierDefault)) |
| + } |
| + if Sizes[2].Limit != TierChildren || Sizes[2].Shares != SharesOf(TierChildren) { |
| + t.Errorf("large = %+v, want TierChildren %+v with shares %+v", Sizes[2], TierChildren, SharesOf(TierChildren)) |
| + } |
| + |
| + for _, w := range want { |
| + got, ok := SizeByName(w.Name) |
| + if !ok || got != w { |
| + t.Errorf("SizeByName(%q) = %+v, %v; want %+v, true", w.Name, got, ok, w) |
| + } |
| + } |
| + for _, name := range []string{"", "Large", "default", "medium"} { |
| + if got, ok := SizeByName(name); ok { |
| + t.Errorf("SizeByName(%q) = %+v, true; want not ok", name, got) |
| + } |
| + } |
| + |
| + if got, ok := SizeOf(TierDefault); !ok || got.Name != "standard" { |
| + t.Errorf("SizeOf(TierDefault) = %+v, %v; want standard", got, ok) |
| + } |
| + if got, ok := SizeOf(TierChildren); !ok || got.Name != "large" { |
| + t.Errorf("SizeOf(TierChildren) = %+v, %v; want large", got, ok) |
| + } |
| + if got, ok := SizeOf(Limit{MemoryMax: 3 << 30}); ok { |
| + t.Errorf("SizeOf(3G) = %+v, true; want not ok", got) |
| + } |
| + |
| + names := SizeNames() |
| + if wantNames := []string{"small", "standard", "large", "xlarge"}; !reflect.DeepEqual(names, wantNames) { |
| + t.Errorf("SizeNames() = %q, want %q", names, wantNames) |
| + } |
| + // A caller that edits the returned slice does not edit the table. |
| + names[0] = "changed" |
| + if Sizes[0].Name != "small" || SizeNames()[0] != "small" { |
| + t.Errorf("SizeNames() shares storage with Sizes") |
| + } |
| +} |
| + |
| +// TestSizesTableIsLiteral pins Astra plan R1-10: plan 3 derives TierDefault, |
| +// TierChildren and SharesOf from Sizes, so an initializer that named any of |
| +// them would then be an initialization cycle. |
| +func TestSizesTableIsLiteral(t *testing.T) { |
| + f, err := parser.ParseFile(token.NewFileSet(), "sizes.go", nil, 0) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + banned := map[string]bool{"TierDefault": true, "TierChildren": true, "SharesOf": true, "HighOf": true} |
| + found := false |
| + for _, decl := range f.Decls { |
| + gen, ok := decl.(*ast.GenDecl) |
| + if !ok || gen.Tok != token.VAR { |
| + continue |
| + } |
| + for _, spec := range gen.Specs { |
| + vs := spec.(*ast.ValueSpec) |
| + for i, name := range vs.Names { |
| + if name.Name != "Sizes" { |
| + continue |
| + } |
| + if i >= len(vs.Values) { |
| + t.Fatalf("Sizes has no initializer") |
| + } |
| + found = true |
| + ast.Inspect(vs.Values[i], func(n ast.Node) bool { |
| + if id, ok := n.(*ast.Ident); ok && banned[id.Name] { |
| + t.Errorf("Sizes initializer names %s; every row must be a literal", id.Name) |
| + } |
| + return true |
| + }) |
| + } |
| + } |
| + } |
| + if !found { |
| + t.Fatalf("no `var Sizes = ...` in sizes.go") |
| + } |
| +} |