View Javadoc
1   /*
2    * Copyright (C) 2017, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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 }