blob: 218abaeffaa006fd7d7fe754d5e1420bb4eb5caf [file] [log] [blame]
Adam Sadovskyb85e3532015-04-08 20:38:27 -07001// Copyright 2015 The Vanadium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package vsync
6
7// Used to ease the setup of Veyron Sync test scenarios.
8// Parses a sync command file and returns a vector of commands to execute.
9//
10// Used by different test replay engines:
11// - dagReplayCommands() executes the parsed commands at the DAG API level.
12// - logReplayCommands() executes the parsed commands at the Log API level.
13
14import (
15 "bufio"
16 "fmt"
17 "os"
18 "strconv"
19 "strings"
20)
21
22const (
23 addLocal = iota
24 addRemote
25 setDevTable
26 linkLocal
27 linkRemote
28)
29
30type syncCommand struct {
31 cmd int
32 objID ObjId
33 version Version
34 parents []Version
35 logrec string
36 devID DeviceId
37 genVec GenVector
38 txID TxId
39 txCount uint32
40 deleted bool
41}
42
43func strToVersion(verStr string) (Version, error) {
44 ver, err := strconv.ParseUint(verStr, 10, 64)
45 if err != nil {
46 return 0, err
47 }
48 return Version(ver), nil
49}
50
51func parseSyncCommands(file string) ([]syncCommand, error) {
52 cmds := []syncCommand{}
53 sf, err := os.Open("testdata/" + file)
54 if err != nil {
55 return nil, err
56 }
57 defer sf.Close()
58
59 scanner := bufio.NewScanner(sf)
60 lineno := 0
61 for scanner.Scan() {
62 lineno++
63 line := strings.TrimSpace(scanner.Text())
64 if line == "" || line[0] == '#' {
65 continue
66 }
67
68 args := strings.Split(line, "|")
69 nargs := len(args)
70
71 switch args[0] {
72 case "addl", "addr":
73 expNargs := 9
74 if nargs != expNargs {
75 return nil, fmt.Errorf("%s:%d: need %d args instead of %d", file, lineno, expNargs, nargs)
76 }
77 version, err := strToVersion(args[2])
78 if err != nil {
79 return nil, fmt.Errorf("%s:%d: invalid version: %s", file, lineno, args[2])
80 }
81 var parents []Version
82 for i := 3; i <= 4; i++ {
83 if args[i] != "" {
84 pver, err := strToVersion(args[i])
85 if err != nil {
86 return nil, fmt.Errorf("%s:%d: invalid parent: %s", file, lineno, args[i])
87 }
88 parents = append(parents, pver)
89 }
90 }
91
92 txID, err := strToTxId(args[6])
93 if err != nil {
94 return nil, fmt.Errorf("%s:%d: invalid TxId: %s", file, lineno, args[6])
95 }
96 txCount, err := strconv.ParseUint(args[7], 10, 32)
97 if err != nil {
98 return nil, fmt.Errorf("%s:%d: invalid tx count: %s", file, lineno, args[7])
99 }
100 del, err := strconv.ParseBool(args[8])
101 if err != nil {
102 return nil, fmt.Errorf("%s:%d: invalid deleted bit: %s", file, lineno, args[8])
103 }
104 cmd := syncCommand{
105 version: version,
106 parents: parents,
107 logrec: args[5],
108 txID: txID,
109 txCount: uint32(txCount),
110 deleted: del,
111 }
112 if args[0] == "addl" {
113 cmd.cmd = addLocal
114 } else {
115 cmd.cmd = addRemote
116 }
117 if cmd.objID, err = strToObjId(args[1]); err != nil {
118 return nil, fmt.Errorf("%s:%d: invalid object ID: %s", file, lineno, args[1])
119 }
120 cmds = append(cmds, cmd)
121
122 case "setdev":
123 expNargs := 3
124 if nargs != expNargs {
125 return nil, fmt.Errorf("%s:%d: need %d args instead of %d", file, lineno, expNargs, nargs)
126 }
127
128 genVec := make(GenVector)
129 for _, elem := range strings.Split(args[2], ",") {
130 kv := strings.Split(elem, ":")
131 if len(kv) != 2 {
132 return nil, fmt.Errorf("%s:%d: invalid gen vector key/val: %s", file, lineno, elem)
133 }
134 genID, err := strToGenId(kv[1])
135 if err != nil {
136 return nil, fmt.Errorf("%s:%d: invalid gen ID: %s", file, lineno, kv[1])
137 }
138 genVec[DeviceId(kv[0])] = genID
139 }
140
141 cmd := syncCommand{cmd: setDevTable, devID: DeviceId(args[1]), genVec: genVec}
142 cmds = append(cmds, cmd)
143
144 case "linkl", "linkr":
145 expNargs := 6
146 if nargs != expNargs {
147 return nil, fmt.Errorf("%s:%d: need %d args instead of %d", file, lineno, expNargs, nargs)
148 }
149
150 version, err := strToVersion(args[2])
151 if err != nil {
152 return nil, fmt.Errorf("%s:%d: invalid version: %s", file, lineno, args[2])
153 }
154 if args[3] == "" {
155 return nil, fmt.Errorf("%s:%d: parent (to-node) version not specified", file, lineno)
156 }
157 if args[4] != "" {
158 return nil, fmt.Errorf("%s:%d: cannot specify a 2nd parent (to-node): %s", file, lineno, args[4])
159 }
160 parent, err := strToVersion(args[3])
161 if err != nil {
162 return nil, fmt.Errorf("%s:%d: invalid parent (to-node) version: %s", file, lineno, args[3])
163 }
164
165 cmd := syncCommand{version: version, parents: []Version{parent}, logrec: args[5]}
166 if args[0] == "linkl" {
167 cmd.cmd = linkLocal
168 } else {
169 cmd.cmd = linkRemote
170 }
171 if cmd.objID, err = strToObjId(args[1]); err != nil {
172 return nil, fmt.Errorf("%s:%d: invalid object ID: %s", file, lineno, args[1])
173 }
174 cmds = append(cmds, cmd)
175
176 default:
177 return nil, fmt.Errorf("%s:%d: invalid operation: %s", file, lineno, args[0])
178 }
179 }
180
181 err = scanner.Err()
182 return cmds, err
183}
184
185func dagReplayCommands(dag *dag, syncfile string) error {
186 cmds, err := parseSyncCommands(syncfile)
187 if err != nil {
188 return err
189 }
190
191 for _, cmd := range cmds {
192 switch cmd.cmd {
193 case addLocal:
194 err = dag.addNode(cmd.objID, cmd.version, false, cmd.deleted, cmd.parents, cmd.logrec, NoTxId)
195 if err != nil {
Adam Sadovsky5413b342015-05-05 12:57:14 -0700196 return fmt.Errorf("cannot add local node %v:%d to DAG: %v", cmd.objID, cmd.version, err)
Adam Sadovskyb85e3532015-04-08 20:38:27 -0700197 }
198 if err := dag.moveHead(cmd.objID, cmd.version); err != nil {
Adam Sadovsky5413b342015-05-05 12:57:14 -0700199 return fmt.Errorf("cannot move head to %v:%d in DAG: %v", cmd.objID, cmd.version, err)
Adam Sadovskyb85e3532015-04-08 20:38:27 -0700200 }
201 dag.flush()
202
203 case addRemote:
204 err = dag.addNode(cmd.objID, cmd.version, true, cmd.deleted, cmd.parents, cmd.logrec, NoTxId)
205 if err != nil {
Adam Sadovsky5413b342015-05-05 12:57:14 -0700206 return fmt.Errorf("cannot add remote node %v:%d to DAG: %v", cmd.objID, cmd.version, err)
Adam Sadovskyb85e3532015-04-08 20:38:27 -0700207 }
208 dag.flush()
209
210 case linkLocal:
211 if err = dag.addParent(cmd.objID, cmd.version, cmd.parents[0], false); err != nil {
Adam Sadovsky5413b342015-05-05 12:57:14 -0700212 return fmt.Errorf("cannot add local parent %d to DAG node %v:%d: %v",
Adam Sadovskyb85e3532015-04-08 20:38:27 -0700213 cmd.parents[0], cmd.objID, cmd.version, err)
214 }
215 dag.flush()
216
217 case linkRemote:
218 if err = dag.addParent(cmd.objID, cmd.version, cmd.parents[0], true); err != nil {
Adam Sadovsky5413b342015-05-05 12:57:14 -0700219 return fmt.Errorf("cannot add remote parent %d to DAG node %v:%d: %v",
Adam Sadovskyb85e3532015-04-08 20:38:27 -0700220 cmd.parents[0], cmd.objID, cmd.version, err)
221 }
222 dag.flush()
223 }
224 }
225 return nil
226}