Author: adulceanu
Date: Mon Feb 5 14:56:37 2018
New Revision: 1823184
URL: http://svn.apache.org/viewvc?rev=1823184&view=rev
Log:
OAK-6373 - oak-run check should also check checkpoints
Modified:
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckInvalidRepositoryTest.java
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckRepositoryTestBase.java
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckValidRepositoryTest.java
Modified:
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/CheckCommand.java
Mon Feb 5 14:56:37 2018
@@ -48,6 +48,10 @@ class CheckCommand implements Command {
ArgumentAcceptingOptionSpec<String> filter = parser.accepts(
"filter", "comma separated content paths to be checked")
.withRequiredArg().ofType(String.class).withValuesSeparatedBy(',').defaultsTo("/");
+ OptionSpec<?> head = parser.accepts("head", "checks only latest /root
(i.e without checkpoints)");
+ ArgumentAcceptingOptionSpec<String> cp = parser.accepts(
+ "checkpoints", "checks only specified checkpoints (comma
separated); use --checkpoints all to check all checkpoints")
+
.withOptionalArg().ofType(String.class).withValuesSeparatedBy(',').defaultsTo("all");
OptionSpec<?> ioStatistics = parser.accepts("io-stats", "Print I/O
statistics (only for oak-segment-tar)");
OptionSet options = parser.parse(args);
@@ -63,13 +67,20 @@ class CheckCommand implements Command {
String journalFileName = journal.value(options);
long debugLevel = notify.value(options);
Set<String> filterPaths = new
LinkedHashSet<String>(filter.values(options));
+ Set<String> checkpoints = new LinkedHashSet<>();
+ if (options.has(cp) || !options.has(head)) {
+ checkpoints.addAll(cp.values(options));
+ }
if (options.has(deep)) {
printUsage(parser, err, "The --deep option was deprecated! Please
do not use it in the future!"
, "A deep scan of the content tree, traversing every node,
will be performed by default.");
}
- SegmentTarUtils.check(dir, journalFileName, debugLevel,
options.has(bin), filterPaths, options.has(ioStatistics), out, err);
+ boolean checkHead = !options.has(cp) || options.has(head);
+
+ SegmentTarUtils.check(dir, journalFileName, debugLevel,
options.has(bin), checkHead, checkpoints, filterPaths,
+ options.has(ioStatistics), out, err);
}
private void printUsage(OptionParser parser, PrintWriter err, String...
messages) throws IOException {
Modified:
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-run/src/main/java/org/apache/jackrabbit/oak/run/SegmentTarUtils.java
Mon Feb 5 14:56:37 2018
@@ -84,13 +84,15 @@ final class SegmentTarUtils {
.run();
}
- static void check(File dir, String journalFileName, long debugLevel,
boolean checkBinaries, Set<String> filterPaths, boolean ioStatistics,
- PrintWriter outWriter, PrintWriter errWriter) {
+ static void check(File dir, String journalFileName, long debugLevel,
boolean checkBinaries, boolean checkHead, Set<String> checkpoints,
+ Set<String> filterPaths, boolean ioStatistics, PrintWriter
outWriter, PrintWriter errWriter) {
Check.builder()
.withPath(dir)
.withJournal(journalFileName)
.withDebugInterval(debugLevel)
.withCheckBinaries(checkBinaries)
+ .withCheckHead(checkHead)
+ .withCheckpoints(checkpoints)
.withFilterPaths(filterPaths)
.withIOStatistics(ioStatistics)
.withOutWriter(outWriter)
@@ -182,4 +184,4 @@ final class SegmentTarUtils {
return
fileStore.getRevisions().getHead().getSegment().getSegmentVersion();
}
-}
+}
\ No newline at end of file
Modified:
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/file/tooling/ConsistencyChecker.java
Mon Feb 5 14:56:37 2018
@@ -19,7 +19,6 @@
package org.apache.jackrabbit.oak.segment.file.tooling;
-import static com.google.common.collect.Maps.newHashMap;
import static java.text.DateFormat.getDateTimeInstance;
import static org.apache.jackrabbit.oak.api.Type.BINARIES;
import static org.apache.jackrabbit.oak.api.Type.BINARY;
@@ -37,24 +36,30 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.text.MessageFormat;
+import java.util.ArrayList;
import java.util.Date;
-import java.util.HashSet;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+import com.google.common.collect.Sets;
import org.apache.jackrabbit.oak.api.Blob;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.segment.SegmentBlob;
+import org.apache.jackrabbit.oak.segment.SegmentNodeStore;
import org.apache.jackrabbit.oak.segment.SegmentNodeStoreBuilders;
import org.apache.jackrabbit.oak.segment.file.FileStore;
import org.apache.jackrabbit.oak.segment.file.FileStoreBuilder;
-import org.apache.jackrabbit.oak.segment.file.tar.IOMonitorAdapter;
import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
import org.apache.jackrabbit.oak.segment.file.JournalEntry;
import org.apache.jackrabbit.oak.segment.file.JournalReader;
import org.apache.jackrabbit.oak.segment.file.ReadOnlyFileStore;
+import org.apache.jackrabbit.oak.segment.file.tar.IOMonitorAdapter;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -65,6 +70,10 @@ import org.apache.jackrabbit.oak.spi.sta
*/
public class ConsistencyChecker implements Closeable {
+ private static final String CHECKPOINT_INDENT = " ";
+
+ private static final String NO_INDENT = "";
+
private static class StatisticsIOMonitor extends IOMonitorAdapter {
private final AtomicLong ioOperations = new AtomicLong(0);
@@ -95,6 +104,8 @@ public class ConsistencyChecker implemen
private int nodeCount;
private int propertyCount;
+
+ private int checkCount;
/**
* Run a full traversal consistency check.
@@ -104,18 +115,23 @@ public class ConsistencyChecker implemen
* @param debugInterval number of seconds between printing progress
information to
* the console during the full traversal phase.
* @param checkBinaries if {@code true} full content of binary
properties will be scanned
+ * @param checkHead if {@code true} will check the head
+ * @param checkpoints collection of checkpoints to be checked
* @param filterPaths collection of repository paths to be checked
* @param ioStatistics if {@code true} prints I/O statistics gathered
while consistency
* check was performed
* @param outWriter text output stream writer
* @param errWriter text error stream writer
* @throws IOException
+ * @throws InvalidFileStoreVersionException
*/
public static void checkConsistency(
File directory,
String journalFileName,
long debugInterval,
boolean checkBinaries,
+ boolean checkHead,
+ Set<String> checkpoints,
Set<String> filterPaths,
boolean ioStatistics,
PrintWriter outWriter,
@@ -125,55 +141,122 @@ public class ConsistencyChecker implemen
JournalReader journal = new JournalReader(new File(directory,
journalFileName));
ConsistencyChecker checker = new ConsistencyChecker(directory,
debugInterval, ioStatistics, outWriter, errWriter)
) {
- Map<String, JournalEntry> pathToJournalEntry = newHashMap();
- Map<String, Set<String>> pathToCorruptPaths = newHashMap();
- for (String path : filterPaths) {
- pathToCorruptPaths.put(path, new HashSet<String>());
- }
+ Set<String> checkpointsSet = Sets.newLinkedHashSet();
+ List<PathToCheck> headPaths = new ArrayList<>();
+ Map<String, List<PathToCheck>> checkpointPaths = new HashMap<>();
- int count = 0;
int revisionCount = 0;
+
+ if (!checkpoints.isEmpty()) {
+ checkpointsSet.addAll(checkpoints);
+
+ if (checkpointsSet.remove("all")) {
+ checkpointsSet = Sets
+
.newLinkedHashSet(SegmentNodeStoreBuilders.builder(checker.store).build().checkpoints());
+ }
+ }
+
+ for (String path : filterPaths) {
+ if (checkHead) {
+ headPaths.add(new PathToCheck(path, null));
+ checker.checkCount++;
+ }
+
+ for (String checkpoint : checkpointsSet) {
+ List<PathToCheck> pathList =
checkpointPaths.get(checkpoint);
+ if (pathList == null) {
+ pathList = new ArrayList<>();
+ checkpointPaths.put(checkpoint, pathList);
+ }
- while (journal.hasNext() && count < filterPaths.size()) {
+ pathList.add(new PathToCheck(path, checkpoint));
+ checker.checkCount++;
+ }
+ }
+
+ int initialCount = checker.checkCount;
+ JournalEntry lastValidJournalEntry = null;
+
+ while (journal.hasNext() && checker.checkCount > 0) {
JournalEntry journalEntry = journal.next();
- checker.print("Checking revision {0}",
journalEntry.getRevision());
+ String revision = journalEntry.getRevision();
try {
revisionCount++;
+ checker.store.setRevision(revision);
+ boolean overallValid = true;
+
+ SegmentNodeStore sns =
SegmentNodeStoreBuilders.builder(checker.store).build();
- for (String path : filterPaths) {
- if (pathToJournalEntry.get(path) == null) {
-
- Set<String> corruptPaths =
pathToCorruptPaths.get(path);
- String corruptPath =
checker.checkPathAtRevision(journalEntry.getRevision(), corruptPaths, path,
checkBinaries);
-
- if (corruptPath == null) {
- checker.print("Path {0} is consistent", path);
- pathToJournalEntry.put(path, journalEntry);
- count++;
- } else {
- corruptPaths.add(corruptPath);
+ checker.print("\nChecking revision {0}", revision);
+
+ if (checkHead) {
+ boolean mustCheck = headPaths.stream().anyMatch(p ->
p.journalEntry == null);
+
+ if (mustCheck) {
+ checker.print("\nChecking head\n");
+ NodeState root = sns.getRoot();
+ overallValid = overallValid &&
checker.checkPathsAtRoot(headPaths, root, journalEntry, checkBinaries);
+ }
+ }
+
+ if (!checkpointsSet.isEmpty()) {
+ Map<String, Boolean> checkpointsToCheck =
checkpointPaths.entrySet().stream().collect(Collectors.toMap(
+ Map.Entry::getKey, e ->
e.getValue().stream().anyMatch(p -> p.journalEntry == null)));
+ boolean mustCheck =
checkpointsToCheck.values().stream().anyMatch(v -> v == true);
+
+ if (mustCheck) {
+ checker.print("\nChecking checkpoints");
+
+ for (String checkpoint : checkpointsSet) {
+ if (checkpointsToCheck.get(checkpoint)) {
+ checker.print("\nChecking checkpoint {0}",
checkpoint);
+
+ List<PathToCheck> pathList =
checkpointPaths.get(checkpoint);
+ NodeState root = sns.retrieve(checkpoint);
+
+ if (root == null) {
+ checker.printError("Checkpoint {0} not
found in this revision!", checkpoint);
+ overallValid = false;
+ } else {
+ overallValid = overallValid &&
checker.checkPathsAtRoot(pathList, root,
+ journalEntry, checkBinaries);
+ }
+ }
}
}
}
+
+ if (overallValid) {
+ lastValidJournalEntry = journalEntry;
+ }
} catch (IllegalArgumentException e) {
- checker.printError("Skipping invalid record id {0}",
journalEntry.getRevision());
+ checker.printError("Skipping invalid record id {0}",
revision);
}
}
- checker.print("Searched through {0} revisions", revisionCount);
+ checker.print("\nSearched through {0} revisions and {1}
checkpoints", revisionCount, checkpointsSet.size());
- if (count == 0) {
+ if (initialCount == checker.checkCount) {
checker.print("No good revision found");
} else {
- for (String path : filterPaths) {
- JournalEntry journalEntry = pathToJournalEntry.get(path);
- String revision = journalEntry != null ?
journalEntry.getRevision() : null;
- long timestamp = journalEntry != null ?
journalEntry.getTimestamp() : -1L;
+ if (checkHead) {
+ checker.print("\nHead");
+ checker.printResults(headPaths, NO_INDENT);
+ }
+
+ if (!checkpointsSet.isEmpty()) {
+ checker.print("\nCheckpoints");
- checker.print("Latest good revision for path {0} is {1}
from {2}", path,
- toString(revision), toString(timestamp));
+ for (String checkpoint : checkpointsSet) {
+ List<PathToCheck> pathList =
checkpointPaths.get(checkpoint);
+ checker.print("- {0}", checkpoint);
+ checker.printResults(pathList, CHECKPOINT_INDENT);
+ }
}
+
+ checker.print("\nOverall");
+ checker.printOverallResults(lastValidJournalEntry);
}
if (ioStatistics) {
@@ -194,6 +277,23 @@ public class ConsistencyChecker implemen
}
}
+ private void printResults(List<PathToCheck> pathList, String indent) {
+ for (PathToCheck ptc : pathList) {
+ String revision = ptc.journalEntry != null ?
ptc.journalEntry.getRevision() : null;
+ long timestamp = ptc.journalEntry != null ?
ptc.journalEntry.getTimestamp() : -1L;
+
+ print("{0}Latest good revision for path {1} is {2} from {3}",
indent, ptc.path,
+ toString(revision), toString(timestamp));
+ }
+ }
+
+ private void printOverallResults(JournalEntry journalEntry) {
+ String revision = journalEntry != null ? journalEntry.getRevision() :
null;
+ long timestamp = journalEntry != null ? journalEntry.getTimestamp() :
-1L;
+
+ print("Latest good revision for paths and checkpoints checked is {0}
from {1}", toString(revision), toString(timestamp));
+ }
+
private static String toString(String revision) {
if (revision != null) {
return revision;
@@ -234,26 +334,50 @@ public class ConsistencyChecker implemen
this.errWriter = errWriter;
}
-
/**
- * Checks the consistency of the supplied {@code paths} at the given
{@code revision},
- * starting first with already known {@code corruptPaths}.
+ * Checks for consistency a list of paths, relative to the same root.
*
- * @param revision revision to be checked
- * @param corruptPaths already known corrupt paths from previous revisions
- * @param path path on which to run consistency check,
- * provided there are no corrupt paths.
+ * @param paths paths to check
+ * @param root root relative to which the paths are retrieved
+ * @param journalEntry entry containing the current revision checked
+ * @param checkBinaries if {@code true} full content of binary
properties will be scanned
+ * @return {@code true}, if the whole list of paths is
consistent
+ */
+ private boolean checkPathsAtRoot(List<PathToCheck> paths, NodeState root,
JournalEntry journalEntry,
+ boolean checkBinaries) {
+ boolean result = true;
+
+ for (PathToCheck ptc : paths) {
+ if (ptc.journalEntry == null) {
+ String corruptPath = checkPathAtRoot(ptc, root, checkBinaries);
+
+ if (corruptPath == null) {
+ print("Path {0} is consistent", ptc.path);
+ ptc.journalEntry = journalEntry;
+ checkCount--;
+ } else {
+ result = false;
+ ptc.corruptPaths.add(corruptPath);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks the consistency of the supplied {@code ptc} relative to the
given {@code root}.
+ *
+ * @param ptc path to check, provided there are no corrupt paths.
+ * @param root root relative to which the path is retrieved
* @param checkBinaries if {@code true} full content of binary properties
will be scanned
- * @return {@code null}, if the content tree rooted at path
is consistent
- * in this revision or the path of the first
inconsistency otherwise.
+ * @return {@code null}, if the content tree rooted at path
(possibly under a checkpoint)
+ * is consistent in this revision or the path of the
first inconsistency otherwise.
*/
- public String checkPathAtRevision(String revision, Set<String>
corruptPaths, String path, boolean checkBinaries) {
+ private String checkPathAtRoot(PathToCheck ptc, NodeState root, boolean
checkBinaries) {
String result = null;
-
- store.setRevision(revision);
- NodeState root =
SegmentNodeStoreBuilders.builder(store).build().getRoot();
- for (String corruptPath : corruptPaths) {
+ for (String corruptPath : ptc.corruptPaths) {
try {
NodeWrapper wrapper =
NodeWrapper.deriveTraversableNodeOnPath(root, corruptPath);
result = checkNode(wrapper.node, wrapper.path, checkBinaries);
@@ -269,17 +393,17 @@ public class ConsistencyChecker implemen
nodeCount = 0;
propertyCount = 0;
- print("Checking {0}", path);
+ print("Checking {0}", ptc.path);
try {
- NodeWrapper wrapper =
NodeWrapper.deriveTraversableNodeOnPath(root, path);
+ NodeWrapper wrapper =
NodeWrapper.deriveTraversableNodeOnPath(root, ptc.path);
result = checkNodeAndDescendants(wrapper.node, wrapper.path,
checkBinaries);
print("Checked {0} nodes and {1} properties", nodeCount,
propertyCount);
return result;
} catch (IllegalArgumentException e) {
- printError("Path {0} not found", path);
- return path;
+ printError("Path {0} not found", ptc.path);
+ return ptc.path;
}
}
@@ -380,6 +504,40 @@ public class ConsistencyChecker implemen
}
}
}
+
+ static class PathToCheck {
+ final String path;
+ final String checkpoint;
+
+ JournalEntry journalEntry;
+ Set<String> corruptPaths = new LinkedHashSet<>();
+
+ PathToCheck(String path, String checkpoint) {
+ this.path = path;
+ this.checkpoint = checkpoint;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((checkpoint == null) ? 0 :
checkpoint.hashCode());
+ result = prime * result + ((path == null) ? 0 : path.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ if (this == object) {
+ return true;
+ } else if (object instanceof PathToCheck) {
+ PathToCheck that = (PathToCheck) object;
+ return path.equals(that.path) &&
checkpoint.equals(that.checkpoint);
+ } else {
+ return false;
+ }
+ }
+ }
private boolean traverse(Blob blob, boolean checkBinaries) throws
IOException {
if (checkBinaries && !isExternal(blob)) {
@@ -425,8 +583,8 @@ public class ConsistencyChecker implemen
outWriter.println(MessageFormat.format(format, arg1, arg2));
}
- private void print(String format, Object arg1, Object arg2, Object arg3) {
- outWriter.println(MessageFormat.format(format, arg1, arg2, arg3));
+ private void print(String format, Object arg1, Object arg2, Object arg3,
Object arg4) {
+ outWriter.println(MessageFormat.format(format, arg1, arg2, arg3,
arg4));
}
private void printError(String format, Object arg) {
Modified:
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-segment-tar/src/main/java/org/apache/jackrabbit/oak/segment/tool/Check.java
Mon Feb 5 14:56:37 2018
@@ -53,6 +53,10 @@ public class Check implements Runnable {
private boolean checkBinaries;
+ private boolean checkHead;
+
+ private Set<String> checkpoints;
+
private Set<String> filterPaths;
private boolean ioStatistics;
@@ -116,6 +120,30 @@ public class Check implements Runnable {
}
/**
+ * Instruct the command to check head state.
+ * This parameter is not required and defaults to {@code true}.
+ * @param checkHead if {@code true}, will check the head state.
+ * @return this builder.
+ */
+ public Builder withCheckHead(boolean checkHead) {
+ this.checkHead = checkHead;
+ return this;
+ }
+
+ /**
+ * Instruct the command to check specified checkpoints.
+ * This parameter is not required and defaults to "/checkpoints",
+ * i.e. will check all checkpoints when not explicitly overridden.
+ *
+ * @param checkpoints checkpoints to be checked
+ * @return this builder.
+ */
+ public Builder withCheckpoints(Set<String> checkpoints) {
+ this.checkpoints = checkpoints;
+ return this;
+ }
+
+ /**
* Content paths to be checked. This parameter is not required and
* defaults to "/".
*
@@ -185,6 +213,10 @@ public class Check implements Runnable {
private final boolean checkBinaries;
+ private final boolean checkHead;
+
+ private final Set<String> checkpoints;
+
private final Set<String> filterPaths;
private final boolean ioStatistics;
@@ -197,7 +229,9 @@ public class Check implements Runnable {
this.path = builder.path;
this.journal = builder.journal;
this.debugInterval = builder.debugInterval;
+ this.checkHead = builder.checkHead;
this.checkBinaries = builder.checkBinaries;
+ this.checkpoints = builder.checkpoints;
this.filterPaths = builder.filterPaths;
this.ioStatistics = builder.ioStatistics;
this.outWriter = builder.outWriter;
@@ -207,7 +241,8 @@ public class Check implements Runnable {
@Override
public void run() {
try {
- ConsistencyChecker.checkConsistency(path, journal, debugInterval,
checkBinaries, filterPaths, ioStatistics, outWriter, errWriter);
+ ConsistencyChecker.checkConsistency(path, journal, debugInterval,
checkBinaries, checkHead, checkpoints, filterPaths,
+ ioStatistics, outWriter, errWriter);
} catch (Exception e) {
e.printStackTrace();
}
Modified:
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckInvalidRepositoryTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckInvalidRepositoryTest.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckInvalidRepositoryTest.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckInvalidRepositoryTest.java
Mon Feb 5 14:56:37 2018
@@ -21,15 +21,15 @@ package org.apache.jackrabbit.oak.segmen
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
+import com.google.common.collect.Lists;
import org.apache.jackrabbit.oak.segment.tool.Check;
import org.junit.Before;
import org.junit.Test;
-import com.google.common.collect.Lists;
-
/**
* Tests for {@link CheckCommand} assuming an invalid repository.
*/
@@ -56,6 +56,8 @@ public class CheckInvalidRepositoryTest
.withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
+ .withCheckHead(true)
+ .withCheckpoints(checkpoints)
.withCheckBinaries(true)
.withFilterPaths(filterPaths)
.withOutWriter(outWriter)
@@ -89,6 +91,8 @@ public class CheckInvalidRepositoryTest
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
.withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(checkpoints)
.withFilterPaths(filterPaths)
.withOutWriter(outWriter)
.withErrWriter(errWriter)
@@ -98,7 +102,7 @@ public class CheckInvalidRepositoryTest
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("No good
revision found"));
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Checking checkpoints", "No good revision found"));
assertExpectedOutput(strErr.toString(),
Lists.newArrayList(
"Error while traversing /z:
java.lang.IllegalArgumentException: Segment reference out of bounds",
@@ -121,6 +125,8 @@ public class CheckInvalidRepositoryTest
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
.withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withFilterPaths(filterPaths)
.withOutWriter(outWriter)
.withErrWriter(errWriter)
@@ -135,4 +141,73 @@ public class CheckInvalidRepositoryTest
assertExpectedOutput(strErr.toString(), Lists.newArrayList(
"Error while traversing /a:
java.lang.IllegalArgumentException: Segment reference out of bounds"));
}
+
+ @Test
+ public void testCorruptHeadWithValidCheckpoints() {
+ StringWriter strOut = new StringWriter();
+ StringWriter strErr = new StringWriter();
+
+ PrintWriter outWriter = new PrintWriter(strOut, true);
+ PrintWriter errWriter = new PrintWriter(strErr, true);
+
+ Set<String> filterPaths = new LinkedHashSet<>();
+ filterPaths.add("/");
+
+ Check.builder()
+ .withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
+ .withJournal("journal.log")
+ .withDebugInterval(Long.MAX_VALUE)
+ .withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(checkpoints)
+ .withFilterPaths(filterPaths)
+ .withOutWriter(outWriter)
+ .withErrWriter(errWriter)
+ .build()
+ .run();
+
+ outWriter.close();
+ errWriter.close();
+
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Checking checkpoints",
+ "Checked 7 nodes and 21 properties", "Path / is consistent",
"Searched through 2 revisions and 2 checkpoints"));
+ assertExpectedOutput(strErr.toString(), Lists.newArrayList(
+ "Error while traversing /a:
java.lang.IllegalArgumentException: Segment reference out of bounds"));
+ }
+
+ @Test
+ public void testCorruptPathInCp1NoValidRevision() throws Exception {
+ corruptPathFromCheckpoint();
+
+ StringWriter strOut = new StringWriter();
+ StringWriter strErr = new StringWriter();
+
+ PrintWriter outWriter = new PrintWriter(strOut, true);
+ PrintWriter errWriter = new PrintWriter(strErr, true);
+
+ Set<String> filterPaths = new LinkedHashSet<>();
+ filterPaths.add("/b");
+
+ Set<String> cps = new HashSet<>();
+ cps.add(checkpoints.iterator().next());
+
+ Check.builder()
+ .withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
+ .withJournal("journal.log")
+ .withDebugInterval(Long.MAX_VALUE)
+ .withCheckBinaries(true)
+ .withCheckpoints(cps)
+ .withFilterPaths(filterPaths)
+ .withOutWriter(outWriter)
+ .withErrWriter(errWriter)
+ .build()
+ .run();
+
+ outWriter.close();
+ errWriter.close();
+
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 2 revisions and 1 checkpoints", "No good revision found"));
+ assertExpectedOutput(strErr.toString(), Lists.newArrayList(
+ "Error while traversing /b:
java.lang.IllegalArgumentException: Segment reference out of bounds"));
+ }
}
Modified:
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckRepositoryTestBase.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckRepositoryTestBase.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckRepositoryTestBase.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckRepositoryTestBase.java
Mon Feb 5 14:56:37 2018
@@ -18,6 +18,8 @@
*/
package org.apache.jackrabbit.oak.segment.file.tooling;
+import static com.google.common.base.Charsets.UTF_8;
+
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -26,10 +28,13 @@ import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
+import java.util.Set;
import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.segment.RecordId;
import org.apache.jackrabbit.oak.segment.RecordType;
import org.apache.jackrabbit.oak.segment.SegmentNodeState;
import org.apache.jackrabbit.oak.segment.SegmentNodeStore;
@@ -57,6 +62,8 @@ public class CheckRepositoryTestBase {
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder(new
File("target"));
+
+ protected Set<String> checkpoints = new LinkedHashSet<>();
@Before
public void setup() throws Exception {
@@ -66,7 +73,7 @@ public class CheckRepositoryTestBase {
protected void addValidRevision() throws InvalidFileStoreVersionException,
IOException, CommitFailedException {
FileStore fileStore =
FileStoreBuilder.fileStoreBuilder(temporaryFolder.getRoot()).withMaxFileSize(256)
.withSegmentCacheSize(64).build();
-
+
SegmentNodeStore nodeStore =
SegmentNodeStoreBuilders.builder(fileStore).build();
NodeBuilder builder = nodeStore.getRoot().builder();
@@ -79,6 +86,13 @@ public class CheckRepositoryTestBase {
addChildWithProperties(nodeStore, builder, "f", 6);
nodeStore.merge(builder, EmptyHook.INSTANCE, CommitInfo.EMPTY);
+
+ // add checkpoints
+ String cp1 = nodeStore.checkpoint(10_000);
+ String cp2 = nodeStore.checkpoint(10_000);
+ checkpoints.add(cp1);
+ checkpoints.add(cp2);
+
fileStore.close();
}
@@ -99,59 +113,88 @@ public class CheckRepositoryTestBase {
// get record number to corrupt (NODE record for "z")
SegmentNodeState child = (SegmentNodeState) after.getChildNode("z");
- int zRecordNumber = child.getRecordId().getRecordNumber();
+ RecordId zRecordId = child.getRecordId();
// get record number to corrupt (NODE record for "a")
child = (SegmentNodeState) after.getChildNode("a");
- int aRecordNumber = child.getRecordId().getRecordNumber();
+ RecordId aRecordId = child.getRecordId();
fileStore.close();
- corruptRecord(zRecordNumber);
- corruptRecord(aRecordNumber);
+ corruptRecord(zRecordId, "data00001a.tar");
+ corruptRecord(aRecordId, "data00001a.tar");
}
+
+ protected void corruptPathFromCheckpoint() throws
InvalidFileStoreVersionException, IOException {
+ FileStore fileStore =
FileStoreBuilder.fileStoreBuilder(temporaryFolder.getRoot()).withMaxFileSize(256)
+ .withSegmentCacheSize(64).build();
- private void corruptRecord(int recordNumber) throws FileNotFoundException,
IOException {
- //since the filestore was closed after writing the first revision,
we're always dealing with the 2nd tar file
- RandomAccessFile file = new RandomAccessFile(new
File(temporaryFolder.getRoot(),"data00001a.tar"), "rw");
-
- // read segment header
- ByteBuffer header = ByteBuffer.allocate(HEADER_SIZE);
- file.readFully(header.array());
-
- // read segment size from header
- byte[] segmentSizeBytes = new byte[11];
- System.arraycopy(header.array(), 124, segmentSizeBytes, 0, 11);
- int size = Integer.parseInt(new String(segmentSizeBytes,
Charset.forName("UTF-8")), 8);
-
+ SegmentNodeStore nodeStore =
SegmentNodeStoreBuilders.builder(fileStore).build();
+ SegmentNodeState cp1 = (SegmentNodeState)
nodeStore.retrieve(checkpoints.iterator().next());
+ RecordId bRecordId = ((SegmentNodeState)
cp1.getChildNode("b")).getRecordId();
+ fileStore.close();
+
+ corruptRecord(bRecordId, "data00000a.tar");
+ }
+
+ private void corruptRecord(RecordId recordId, String tarFileName) throws
FileNotFoundException, IOException {
+ RandomAccessFile file = new RandomAccessFile(new
File(temporaryFolder.getRoot(), tarFileName), "rw");
+
+ String segmentName = recordId.getSegmentId().toString();
+ String crtEntryName = "";
+ int entrySize = 0;
+ long filePointer = 0;
+
+ while(!crtEntryName.equals(segmentName)) {
+ filePointer = file.getFilePointer();
+ // read entry header
+ ByteBuffer entryHeader = ByteBuffer.allocate(HEADER_SIZE);
+ file.readFully(entryHeader.array());
+
+ // read entry size from header
+ byte[] crtEntryNameBytes = new byte[100];
+ System.arraycopy(entryHeader.array(), 0, crtEntryNameBytes, 0,
100);
+ crtEntryName = new String(crtEntryNameBytes, 0, 100, UTF_8);
+ crtEntryName = crtEntryName.substring(0,
crtEntryName.indexOf('.'));
+
+ byte[] entrySizeBytes = new byte[11];
+ System.arraycopy(entryHeader.array(), 124, entrySizeBytes, 0, 11);
+ entrySize = Integer.parseInt(new String(entrySizeBytes,
Charset.forName("UTF-8")), 8);
+
+ if (!crtEntryName.equals(segmentName)) {
+ file.skipBytes(entrySize);
+ file.skipBytes(HEADER_SIZE - (entrySize % HEADER_SIZE));
+ }
+ };
+
// read actual segment
- ByteBuffer segmentBytes = ByteBuffer.allocate(size);
+ ByteBuffer segmentBytes = ByteBuffer.allocate(entrySize);
file.readFully(segmentBytes.array());
int segmentRefs = segmentBytes.getInt(14);
// read the header for our record
- int skip = 32 + segmentRefs * 16 + recordNumber * 9;
+ int skip = 32 + segmentRefs * 16 + recordId.getRecordNumber() * 9;
int number = segmentBytes.getInt(skip);
byte type = segmentBytes.get(skip + 4);
int offset = segmentBytes.getInt(skip + 4 + 1);
- Assert.assertEquals(recordNumber, number);
+ Assert.assertEquals(recordId.getRecordNumber(), number);
Assert.assertEquals(RecordType.NODE.ordinal(), type);
// read the offset of previous record to derive length of our record
- int prevSkip = 32 + segmentRefs * 16 + (recordNumber - 1) * 9;
+ int prevSkip = 32 + segmentRefs * 16 + (recordId.getRecordNumber() -
1) * 9;
int prevOffset = segmentBytes.getInt(prevSkip + 4 + 1);
int length = prevOffset - offset;
- int realOffset = size - (MAX_SEGMENT_SIZE - offset);
+ int realOffset = entrySize - (MAX_SEGMENT_SIZE - offset);
// write random bytes inside the NODE record to corrupt it
Random r = new Random(10);
byte[] bogusData = new byte[length];
r.nextBytes(bogusData);
- file.seek(HEADER_SIZE + realOffset);
+ file.seek(filePointer + HEADER_SIZE + realOffset);
file.write(bogusData);
file.close();
Modified:
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckValidRepositoryTest.java
URL:
http://svn.apache.org/viewvc/jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckValidRepositoryTest.java?rev=1823184&r1=1823183&r2=1823184&view=diff
==============================================================================
---
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckValidRepositoryTest.java
(original)
+++
jackrabbit/oak/branches/1.8/oak-segment-tar/src/test/java/org/apache/jackrabbit/oak/segment/file/tooling/CheckValidRepositoryTest.java
Mon Feb 5 14:56:37 2018
@@ -21,14 +21,14 @@ package org.apache.jackrabbit.oak.segmen
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
+import com.google.common.collect.Lists;
import org.apache.jackrabbit.oak.segment.tool.Check;
import org.junit.Test;
-import com.google.common.collect.Lists;
-
/**
* Tests for {@link CheckCommand} assuming a consistent repository.
*/
@@ -50,6 +50,8 @@ public class CheckValidRepositoryTest ex
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
.withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withFilterPaths(filterPaths)
.withIOStatistics(true)
.withOutWriter(outWriter)
@@ -60,8 +62,8 @@ public class CheckValidRepositoryTest ex
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 1 revisions", "Checked 7 nodes and 21 properties",
- "Path / is consistent"));
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Searched through 1 revisions and 0 checkpoints",
+ "Checked 7 nodes and 21 properties", "Path / is consistent"));
assertExpectedOutput(strErr.toString(), Lists.newArrayList(""));
}
@@ -86,6 +88,8 @@ public class CheckValidRepositoryTest ex
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
.withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withFilterPaths(filterPaths)
.withIOStatistics(true)
.withOutWriter(outWriter)
@@ -96,7 +100,7 @@ public class CheckValidRepositoryTest ex
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 1 revisions",
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Searched through 1 revisions and 0 checkpoints",
"Checked 1 nodes and 1 properties", "Checked 1 nodes and 2
properties", "Checked 1 nodes and 3 properties",
"Path /a is consistent", "Path /b is consistent", "Path /c is
consistent", "Path /d is consistent", "Path /e is consistent",
"Path /f is consistent"));
@@ -118,6 +122,8 @@ public class CheckValidRepositoryTest ex
.withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withFilterPaths(filterPaths)
.withIOStatistics(true)
.withOutWriter(outWriter)
@@ -128,8 +134,8 @@ public class CheckValidRepositoryTest ex
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 1 revisions", "Checked 7 nodes and 15 properties",
- "Path / is consistent"));
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Searched through 1 revisions and 0 checkpoints",
+ "Checked 7 nodes and 15 properties", "Path / is consistent"));
assertExpectedOutput(strErr.toString(), Lists.newArrayList(""));
}
@@ -151,6 +157,8 @@ public class CheckValidRepositoryTest ex
.withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withFilterPaths(filterPaths)
.withIOStatistics(true)
.withOutWriter(outWriter)
@@ -161,7 +169,7 @@ public class CheckValidRepositoryTest ex
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 1 revisions", "Checked 1 nodes and 0 properties",
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Searched through 1 revisions and 0 checkpoints",
"Checked 1 nodes and 0 properties", "Checked 1 nodes and 4
properties", "Checked 1 nodes and 5 properties",
"Path /a is consistent", "Path /b is consistent", "Path /d is
consistent", "Path /e is consistent"));
assertExpectedOutput(strErr.toString(), Lists.newArrayList(""));
@@ -182,6 +190,8 @@ public class CheckValidRepositoryTest ex
.withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
.withJournal("journal.log")
.withDebugInterval(Long.MAX_VALUE)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withFilterPaths(filterPaths)
.withIOStatistics(true)
.withOutWriter(outWriter)
@@ -192,7 +202,8 @@ public class CheckValidRepositoryTest ex
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 1 revisions", "No good revision found"));
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Searched through 1 revisions and 0 checkpoints",
+ "No good revision found"));
assertExpectedOutput(strErr.toString(), Lists.newArrayList("Path /g
not found"));
}
@@ -217,6 +228,8 @@ public class CheckValidRepositoryTest ex
.withDebugInterval(Long.MAX_VALUE)
.withFilterPaths(filterPaths)
.withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(new HashSet<String>())
.withIOStatistics(true)
.withOutWriter(outWriter)
.withErrWriter(errWriter)
@@ -226,9 +239,111 @@ public class CheckValidRepositoryTest ex
outWriter.close();
errWriter.close();
- assertExpectedOutput(strOut.toString(), Lists.newArrayList("Searched
through 1 revisions", "Checked 1 nodes and 1 properties",
- "Checked 1 nodes and 6 properties", "Checked 1 nodes and 4
properties", "Checked 1 nodes and 5 properties",
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Searched through 1 revisions and 0 checkpoints",
+ "Checked 1 nodes and 1 properties", "Checked 1 nodes and 6
properties", "Checked 1 nodes and 4 properties",
+ "Checked 1 nodes and 5 properties",
"Path /a is consistent", "Path /f is consistent", "Path /d is
consistent", "Path /e is consistent"));
assertExpectedOutput(strErr.toString(), Lists.newArrayList("Path /g
not found"));
}
+
+ @Test
+ public void testSuccessfulCheckOfHeadAndCheckpointsWithoutFilterPaths()
throws Exception {
+ StringWriter strOut = new StringWriter();
+ StringWriter strErr = new StringWriter();
+
+ PrintWriter outWriter = new PrintWriter(strOut, true);
+ PrintWriter errWriter = new PrintWriter(strErr, true);
+
+ Set<String> filterPaths = new LinkedHashSet<>();
+ filterPaths.add("/");
+
+ Check.builder()
+ .withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
+ .withJournal("journal.log")
+ .withDebugInterval(Long.MAX_VALUE)
+ .withFilterPaths(filterPaths)
+ .withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(checkpoints)
+ .withIOStatistics(true)
+ .withOutWriter(outWriter)
+ .withErrWriter(errWriter)
+ .build()
+ .run();
+
+ outWriter.close();
+ errWriter.close();
+
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Checking checkpoints",
+ "Searched through 1 revisions and 2 checkpoints", "Checked 7
nodes and 21 properties", "Path / is consistent"));
+ assertExpectedOutput(strErr.toString(), Lists.newArrayList(""));
+ }
+
+ @Test
+ public void testSuccessfulCheckOfHeadAndCheckpointsWithFilterPaths()
throws Exception {
+ StringWriter strOut = new StringWriter();
+ StringWriter strErr = new StringWriter();
+
+ PrintWriter outWriter = new PrintWriter(strOut, true);
+ PrintWriter errWriter = new PrintWriter(strErr, true);
+
+ Set<String> filterPaths = new LinkedHashSet<>();
+ filterPaths.add("/f");
+
+ Check.builder()
+ .withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
+ .withJournal("journal.log")
+ .withDebugInterval(Long.MAX_VALUE)
+ .withFilterPaths(filterPaths)
+ .withCheckBinaries(true)
+ .withCheckHead(true)
+ .withCheckpoints(checkpoints)
+ .withIOStatistics(true)
+ .withOutWriter(outWriter)
+ .withErrWriter(errWriter)
+ .build()
+ .run();
+
+ outWriter.close();
+ errWriter.close();
+
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
head", "Checking checkpoints",
+ "Searched through 1 revisions and 2 checkpoints", "Checked 1
nodes and 6 properties", "Path /f is consistent"));
+ assertExpectedOutput(strErr.toString(), Lists.newArrayList(""));
+ }
+
+ @Test
+ public void testMissingCheckpointCheck() throws Exception {
+ StringWriter strOut = new StringWriter();
+ StringWriter strErr = new StringWriter();
+
+ PrintWriter outWriter = new PrintWriter(strOut, true);
+ PrintWriter errWriter = new PrintWriter(strErr, true);
+
+ Set<String> filterPaths = new LinkedHashSet<>();
+ filterPaths.add("/");
+
+ HashSet<String> checkpoints = new HashSet<String>();
+ checkpoints.add("bogus-checkpoint");
+
+ Check.builder()
+ .withPath(new File(temporaryFolder.getRoot().getAbsolutePath()))
+ .withJournal("journal.log")
+ .withDebugInterval(Long.MAX_VALUE)
+ .withFilterPaths(filterPaths)
+ .withCheckBinaries(true)
+ .withCheckpoints(checkpoints)
+ .withIOStatistics(true)
+ .withOutWriter(outWriter)
+ .withErrWriter(errWriter)
+ .build()
+ .run();
+
+ outWriter.close();
+ errWriter.close();
+
+ assertExpectedOutput(strOut.toString(), Lists.newArrayList("Checking
checkpoints", "Searched through 1 revisions and 1 checkpoints",
+ "No good revision found"));
+ assertExpectedOutput(strErr.toString(), Lists.newArrayList("Checkpoint
bogus-checkpoint not found in this revision!"));
+ }
}