carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "go/ast"
5 "go/parser"
6 "go/token"
7 "sort"
8 "strings"
9 "testing"
10)
11
12// TestEveryRenameAndFinishIsGuarded is the invariant of moves Task 13 (fix
13// round 3) as a scan of systemapply.go: every os.Rename (an executable put
14// in place), every a.finish( and every a.removeJournal() or
15// a.closeGuardJournal() (a journal finished; closeGuardJournal is the claim
16// release floor's close from Plan 8, which removes the journal and then
17// completes the guard transaction) sits inside a function literal passed to
18// a.activationGuard,
19// unless the table below names it and says why. A new call that is
20// neither fails here, and so does an exemption no call uses any more.
21func TestEveryRenameAndFinishIsGuarded(t *testing.T) {
22 exempt := map[string]string{
23 // The ordinary install finish follows the guarded rename: the
24 // floor protects activation, and the rename is the activation.
25 "install a.finish": "after the guarded rename",
26 // Inside finish itself; its callers are guarded or exempt.
27 "finish a.closeGuardJournal": "finish is the guarded or exempt step",
28 // The install never reached its rename: nothing was activated.
29 "abandonInstall a.removeJournal": "no executable changed",
30 // A legacy backing-up journal: nothing on the box had changed.
31 "recoverIfNeeded a.closeGuardJournal (legacy backing-up)": "no executable changed",
32 }
33 // Sites that must be guarded, so a guard removed from one is caught even
34 // if someone also adds it to the exemptions.
35 mustGuard := []string{
36 "install os.Rename",
37 "restoreFiles os.Rename",
38 "recoverIfNeeded a.finish",
39 "recoverIfNeeded a.closeGuardRollback",
40 "rollback a.closeGuardRollback",
41 }
42
43 fset := token.NewFileSet()
44 file, err := parser.ParseFile(fset, "systemapply.go", nil, parser.ParseComments)
45 if err != nil {
46 t.Fatal(err)
47 }
48 guarded := map[string]int{}
49 used := map[string]bool{}
50 var unguarded []string
51 for _, decl := range file.Decls {
52 fn, ok := decl.(*ast.FuncDecl)
53 if !ok || fn.Body == nil {
54 continue
55 }
56 var stack []ast.Node
57 ast.Inspect(fn.Body, func(n ast.Node) bool {
58 if n == nil {
59 stack = stack[:len(stack)-1]
60 return true
61 }
62 stack = append(stack, n)
63 call, ok := n.(*ast.CallExpr)
64 if !ok {
65 return true
66 }
67 name := callName(call)
68 if name != "os.Rename" && name != "a.finish" && name != "a.removeJournal" && name != "a.closeGuardJournal" && name != "a.closeGuardRollback" {
69 return true
70 }
71 site := fn.Name.Name + " " + name
72 if insideGuard(stack) {
73 guarded[site]++
74 return true
75 }
76 key := site
77 if _, ok := exempt[key]; !ok && fn.Name.Name == "recoverIfNeeded" && (name == "a.removeJournal" || name == "a.closeGuardJournal") && legacyBranch(stack) {
78 key = site + " (legacy backing-up)"
79 }
80 if _, ok := exempt[key]; ok {
81 used[key] = true
82 return true
83 }
84 unguarded = append(unguarded, site+" at "+fset.Position(call.Pos()).String())
85 return true
86 })
87 }
88 sort.Strings(unguarded)
89 for _, u := range unguarded {
90 t.Errorf("%s is outside activationGuard and not exempt", u)
91 }
92 for _, s := range mustGuard {
93 if guarded[s] == 0 {
94 t.Errorf("%s is not inside activationGuard", s)
95 }
96 }
97 for k := range exempt {
98 if !used[k] {
99 t.Errorf("exemption %q matches no call any more", k)
100 }
101 }
102}
103
104func callName(call *ast.CallExpr) string {
105 sel, ok := call.Fun.(*ast.SelectorExpr)
106 if !ok {
107 return ""
108 }
109 if id, ok := sel.X.(*ast.Ident); ok {
110 return id.Name + "." + sel.Sel.Name
111 }
112 return ""
113}
114
115// insideGuard: some enclosing function literal is an argument of a call to
116// a.activationGuard.
117func insideGuard(stack []ast.Node) bool {
118 for i := len(stack) - 1; i > 0; i-- {
119 lit, ok := stack[i].(*ast.FuncLit)
120 if !ok {
121 continue
122 }
123 if call, ok := stack[i-1].(*ast.CallExpr); ok && callName(call) == "a.activationGuard" {
124 for _, arg := range call.Args {
125 if arg == lit {
126 return true
127 }
128 }
129 }
130 }
131 return false
132}
133
134// legacyBranch: the call sits in the `if j.Phase == systemPhaseBackingUp`
135// branch.
136func legacyBranch(stack []ast.Node) bool {
137 for _, n := range stack {
138 if ifs, ok := n.(*ast.IfStmt); ok {
139 if be, ok := ifs.Cond.(*ast.BinaryExpr); ok {
140 if id, ok := be.Y.(*ast.Ident); ok && strings.Contains(id.Name, "BackingUp") {
141 return true
142 }
143 }
144 }
145 }
146 return false
147}
148