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  	/** {@inheritDoc} */
79  	@Override
80  	protected void run() throws Exception {
81  		List<Ref> refs = WriteReftable.readRefs(lsRemotePath);
82  
83  		try (FileInputStream in = new FileInputStream(reftablePath);
84  				BlockSource src = BlockSource.from(in);
85  				ReftableReader reader = new ReftableReader(src)) {
86  			scan(refs, reader);
87  			seek(refs, reader);
88  			byId(refs, reader);
89  		}
90  	}
91  
92  	@SuppressWarnings("nls")
93  	private void scan(List<Ref> refs, ReftableReader reader)
94  			throws IOException {
95  		errw.print(String.format("%-20s", "sequential scan..."));
96  		errw.flush();
97  		try (RefCursor rc = reader.allRefs()) {
98  			for (Ref exp : refs) {
99  				verify(exp, rc);
100 			}
101 			if (rc.next()) {
102 				throw die("expected end of table");
103 			}
104 		}
105 		errw.println(" OK");
106 	}
107 
108 	@SuppressWarnings("nls")
109 	private void seek(List<Ref> refs, ReftableReader reader)
110 			throws IOException {
111 		List<Ref> rnd = new ArrayList<>(refs);
112 		Collections.shuffle(rnd, new Random(SEED1));
113 
114 		TextProgressMonitor pm = new TextProgressMonitor(errw);
115 		pm.beginTask("random seek", rnd.size());
116 		for (Ref exp : rnd) {
117 			try (RefCursor rc = reader.seekRef(exp.getName())) {
118 				verify(exp, rc);
119 				if (rc.next()) {
120 					throw die("should not have ref after " + exp.getName());
121 				}
122 			}
123 			pm.update(1);
124 		}
125 		pm.endTask();
126 	}
127 
128 	@SuppressWarnings("nls")
129 	private void byId(List<Ref> refs, ReftableReader reader)
130 			throws IOException {
131 		Map<ObjectId, List<Ref>> want = groupById(refs);
132 		List<List<Ref>> rnd = new ArrayList<>(want.values());
133 		Collections.shuffle(rnd, new Random(SEED2));
134 
135 		TextProgressMonitor pm = new TextProgressMonitor(errw);
136 		pm.beginTask("byObjectId", rnd.size());
137 		for (List<Ref> exp : rnd) {
138 			Collections.sort(exp, RefComparator.INSTANCE);
139 			ObjectId id = exp.get(0).getObjectId();
140 			try (RefCursor rc = reader.byObjectId(id)) {
141 				for (Ref r : exp) {
142 					verify(r, rc);
143 				}
144 			}
145 			pm.update(1);
146 		}
147 		pm.endTask();
148 	}
149 
150 	private static Map<ObjectId, List<Ref>> groupById(List<Ref> refs) {
151 		Map<ObjectId, List<Ref>> m = new HashMap<>();
152 		for (Ref r : refs) {
153 			ObjectId id = r.getObjectId();
154 			if (id != null) {
155 				List<Ref> c = m.get(id);
156 				if (c == null) {
157 					c = new ArrayList<>(2);
158 					m.put(id, c);
159 				}
160 				c.add(r);
161 			}
162 		}
163 		return m;
164 	}
165 
166 	@SuppressWarnings("nls")
167 	private void verify(Ref exp, RefCursor rc) throws IOException {
168 		if (!rc.next()) {
169 			throw die("ended before " + exp.getName());
170 		}
171 
172 		Ref act = rc.getRef();
173 		if (!exp.getName().equals(act.getName())) {
174 			throw die(String.format("expected %s, found %s",
175 					exp.getName(),
176 					act.getName()));
177 		}
178 
179 		if (exp.isSymbolic()) {
180 			if (!act.isSymbolic()) {
181 				throw die("expected " + act.getName() + " to be symbolic");
182 			}
183 			if (!exp.getTarget().getName().equals(act.getTarget().getName())) {
184 				throw die(String.format("expected %s to be %s, found %s",
185 						exp.getName(),
186 						exp.getLeaf().getName(),
187 						act.getLeaf().getName()));
188 			}
189 			return;
190 		}
191 
192 		if (!AnyObjectId.equals(exp.getObjectId(), act.getObjectId())) {
193 			throw die(String.format("expected %s to be %s, found %s",
194 					exp.getName(),
195 					id(exp.getObjectId()),
196 					id(act.getObjectId())));
197 		}
198 
199 		if (exp.getPeeledObjectId() != null
200 				&& !AnyObjectId.equals(exp.getPeeledObjectId(), act.getPeeledObjectId())) {
201 			throw die(String.format("expected %s to be %s, found %s",
202 					exp.getName(),
203 					id(exp.getPeeledObjectId()),
204 					id(act.getPeeledObjectId())));
205 		}
206 	}
207 
208 	@SuppressWarnings("nls")
209 	private static String id(ObjectId id) {
210 		return id != null ? id.name() : "<null>";
211 	}
212 }