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!"));
+    }
 }


Reply via email to