1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.pgm.debug;
45
46 import java.io.FileInputStream;
47 import java.io.IOException;
48 import java.util.ArrayList;
49 import java.util.Collections;
50 import java.util.HashMap;
51 import java.util.List;
52 import java.util.Map;
53 import java.util.Random;
54
55 import org.eclipse.jgit.internal.storage.io.BlockSource;
56 import org.eclipse.jgit.internal.storage.reftable.RefCursor;
57 import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
58 import org.eclipse.jgit.lib.AnyObjectId;
59 import org.eclipse.jgit.lib.ObjectId;
60 import org.eclipse.jgit.lib.Ref;
61 import org.eclipse.jgit.lib.RefComparator;
62 import org.eclipse.jgit.lib.TextProgressMonitor;
63 import org.eclipse.jgit.pgm.Command;
64 import org.eclipse.jgit.pgm.TextBuiltin;
65 import org.kohsuke.args4j.Argument;
66
67 @Command
68 class VerifyReftable extends TextBuiltin {
69 private static final long SEED1 = 0xaba8bb4de4caf86cL;
70 private static final long SEED2 = 0x28bb5c25ad43ecb5L;
71
72 @Argument(index = 0)
73 private String lsRemotePath;
74
75 @Argument(index = 1)
76 private String reftablePath;
77
78 @Override
79 protected void run() throws Exception {
80 List<Ref> refs = WriteReftable.readRefs(lsRemotePath);
81
82 try (FileInputStream in = new FileInputStream(reftablePath);
83 BlockSource src = BlockSource.from(in);
84 ReftableReader reader = new ReftableReader(src)) {
85 scan(refs, reader);
86 seek(refs, reader);
87 byId(refs, reader);
88 }
89 }
90
91 @SuppressWarnings("nls")
92 private void scan(List<Ref> refs, ReftableReader reader)
93 throws IOException {
94 errw.print(String.format("%-20s", "sequential scan..."));
95 errw.flush();
96 try (RefCursor rc = reader.allRefs()) {
97 for (Ref exp : refs) {
98 verify(exp, rc);
99 }
100 if (rc.next()) {
101 throw die("expected end of table");
102 }
103 }
104 errw.println(" OK");
105 }
106
107 @SuppressWarnings("nls")
108 private void seek(List<Ref> refs, ReftableReader reader)
109 throws IOException {
110 List<Ref> rnd = new ArrayList<>(refs);
111 Collections.shuffle(rnd, new Random(SEED1));
112
113 TextProgressMonitor pm = new TextProgressMonitor(errw);
114 pm.beginTask("random seek", rnd.size());
115 for (Ref exp : rnd) {
116 try (RefCursor rc = reader.seekRef(exp.getName())) {
117 verify(exp, rc);
118 if (rc.next()) {
119 throw die("should not have ref after " + exp.getName());
120 }
121 }
122 pm.update(1);
123 }
124 pm.endTask();
125 }
126
127 @SuppressWarnings("nls")
128 private void byId(List<Ref> refs, ReftableReader reader)
129 throws IOException {
130 Map<ObjectId, List<Ref>> want = groupById(refs);
131 List<List<Ref>> rnd = new ArrayList<>(want.values());
132 Collections.shuffle(rnd, new Random(SEED2));
133
134 TextProgressMonitor pm = new TextProgressMonitor(errw);
135 pm.beginTask("byObjectId", rnd.size());
136 for (List<Ref> exp : rnd) {
137 Collections.sort(exp, RefComparator.INSTANCE);
138 ObjectId id = exp.get(0).getObjectId();
139 try (RefCursor rc = reader.byObjectId(id)) {
140 for (Ref r : exp) {
141 verify(r, rc);
142 }
143 }
144 pm.update(1);
145 }
146 pm.endTask();
147 }
148
149 private static Map<ObjectId, List<Ref>> groupById(List<Ref> refs) {
150 Map<ObjectId, List<Ref>> m = new HashMap<>();
151 for (Ref r : refs) {
152 ObjectId id = r.getObjectId();
153 if (id != null) {
154 List<Ref> c = m.get(id);
155 if (c == null) {
156 c = new ArrayList<>(2);
157 m.put(id, c);
158 }
159 c.add(r);
160 }
161 }
162 return m;
163 }
164
165 @SuppressWarnings("nls")
166 private void verify(Ref exp, RefCursor rc) throws IOException {
167 if (!rc.next()) {
168 throw die("ended before " + exp.getName());
169 }
170
171 Ref act = rc.getRef();
172 if (!exp.getName().equals(act.getName())) {
173 throw die(String.format("expected %s, found %s",
174 exp.getName(),
175 act.getName()));
176 }
177
178 if (exp.isSymbolic()) {
179 if (!act.isSymbolic()) {
180 throw die("expected " + act.getName() + " to be symbolic");
181 }
182 if (!exp.getTarget().getName().equals(act.getTarget().getName())) {
183 throw die(String.format("expected %s to be %s, found %s",
184 exp.getName(),
185 exp.getLeaf().getName(),
186 act.getLeaf().getName()));
187 }
188 return;
189 }
190
191 if (!AnyObjectId.equals(exp.getObjectId(), act.getObjectId())) {
192 throw die(String.format("expected %s to be %s, found %s",
193 exp.getName(),
194 id(exp.getObjectId()),
195 id(act.getObjectId())));
196 }
197
198 if (exp.getPeeledObjectId() != null
199 && !AnyObjectId.equals(exp.getPeeledObjectId(), act.getPeeledObjectId())) {
200 throw die(String.format("expected %s to be %s, found %s",
201 exp.getName(),
202 id(exp.getPeeledObjectId()),
203 id(act.getPeeledObjectId())));
204 }
205 }
206
207 @SuppressWarnings("nls")
208 private static String id(ObjectId id) {
209 return id != null ? id.name() : "<null>";
210 }
211 }