VerifyReftable.java

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

  43. package org.eclipse.jgit.pgm.debug;

  44. import java.io.FileInputStream;
  45. import java.io.IOException;
  46. import java.util.ArrayList;
  47. import java.util.Collections;
  48. import java.util.HashMap;
  49. import java.util.List;
  50. import java.util.Map;
  51. import java.util.Random;

  52. import org.eclipse.jgit.internal.storage.io.BlockSource;
  53. import org.eclipse.jgit.internal.storage.reftable.RefCursor;
  54. import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
  55. import org.eclipse.jgit.lib.AnyObjectId;
  56. import org.eclipse.jgit.lib.ObjectId;
  57. import org.eclipse.jgit.lib.Ref;
  58. import org.eclipse.jgit.lib.RefComparator;
  59. import org.eclipse.jgit.lib.TextProgressMonitor;
  60. import org.eclipse.jgit.pgm.Command;
  61. import org.eclipse.jgit.pgm.TextBuiltin;
  62. import org.kohsuke.args4j.Argument;

  63. @Command
  64. class VerifyReftable extends TextBuiltin {
  65.     private static final long SEED1 = 0xaba8bb4de4caf86cL;
  66.     private static final long SEED2 = 0x28bb5c25ad43ecb5L;

  67.     @Argument(index = 0)
  68.     private String lsRemotePath;

  69.     @Argument(index = 1)
  70.     private String reftablePath;

  71.     /** {@inheritDoc} */
  72.     @Override
  73.     protected void run() throws Exception {
  74.         List<Ref> refs = WriteReftable.readRefs(lsRemotePath);

  75.         try (FileInputStream in = new FileInputStream(reftablePath);
  76.                 BlockSource src = BlockSource.from(in);
  77.                 ReftableReader reader = new ReftableReader(src)) {
  78.             scan(refs, reader);
  79.             seek(refs, reader);
  80.             byId(refs, reader);
  81.         }
  82.     }

  83.     @SuppressWarnings("nls")
  84.     private void scan(List<Ref> refs, ReftableReader reader)
  85.             throws IOException {
  86.         errw.print(String.format("%-20s", "sequential scan..."));
  87.         errw.flush();
  88.         try (RefCursor rc = reader.allRefs()) {
  89.             for (Ref exp : refs) {
  90.                 verify(exp, rc);
  91.             }
  92.             if (rc.next()) {
  93.                 throw die("expected end of table");
  94.             }
  95.         }
  96.         errw.println(" OK");
  97.     }

  98.     @SuppressWarnings("nls")
  99.     private void seek(List<Ref> refs, ReftableReader reader)
  100.             throws IOException {
  101.         List<Ref> rnd = new ArrayList<>(refs);
  102.         Collections.shuffle(rnd, new Random(SEED1));

  103.         TextProgressMonitor pm = new TextProgressMonitor(errw);
  104.         pm.beginTask("random seek", rnd.size());
  105.         for (Ref exp : rnd) {
  106.             try (RefCursor rc = reader.seekRef(exp.getName())) {
  107.                 verify(exp, rc);
  108.                 if (rc.next()) {
  109.                     throw die("should not have ref after " + exp.getName());
  110.                 }
  111.             }
  112.             pm.update(1);
  113.         }
  114.         pm.endTask();
  115.     }

  116.     @SuppressWarnings("nls")
  117.     private void byId(List<Ref> refs, ReftableReader reader)
  118.             throws IOException {
  119.         Map<ObjectId, List<Ref>> want = groupById(refs);
  120.         List<List<Ref>> rnd = new ArrayList<>(want.values());
  121.         Collections.shuffle(rnd, new Random(SEED2));

  122.         TextProgressMonitor pm = new TextProgressMonitor(errw);
  123.         pm.beginTask("byObjectId", rnd.size());
  124.         for (List<Ref> exp : rnd) {
  125.             Collections.sort(exp, RefComparator.INSTANCE);
  126.             ObjectId id = exp.get(0).getObjectId();
  127.             try (RefCursor rc = reader.byObjectId(id)) {
  128.                 for (Ref r : exp) {
  129.                     verify(r, rc);
  130.                 }
  131.             }
  132.             pm.update(1);
  133.         }
  134.         pm.endTask();
  135.     }

  136.     private static Map<ObjectId, List<Ref>> groupById(List<Ref> refs) {
  137.         Map<ObjectId, List<Ref>> m = new HashMap<>();
  138.         for (Ref r : refs) {
  139.             ObjectId id = r.getObjectId();
  140.             if (id != null) {
  141.                 List<Ref> c = m.get(id);
  142.                 if (c == null) {
  143.                     c = new ArrayList<>(2);
  144.                     m.put(id, c);
  145.                 }
  146.                 c.add(r);
  147.             }
  148.         }
  149.         return m;
  150.     }

  151.     @SuppressWarnings("nls")
  152.     private void verify(Ref exp, RefCursor rc) throws IOException {
  153.         if (!rc.next()) {
  154.             throw die("ended before " + exp.getName());
  155.         }

  156.         Ref act = rc.getRef();
  157.         if (!exp.getName().equals(act.getName())) {
  158.             throw die(String.format("expected %s, found %s",
  159.                     exp.getName(),
  160.                     act.getName()));
  161.         }

  162.         if (exp.isSymbolic()) {
  163.             if (!act.isSymbolic()) {
  164.                 throw die("expected " + act.getName() + " to be symbolic");
  165.             }
  166.             if (!exp.getTarget().getName().equals(act.getTarget().getName())) {
  167.                 throw die(String.format("expected %s to be %s, found %s",
  168.                         exp.getName(),
  169.                         exp.getLeaf().getName(),
  170.                         act.getLeaf().getName()));
  171.             }
  172.             return;
  173.         }

  174.         if (!AnyObjectId.equals(exp.getObjectId(), act.getObjectId())) {
  175.             throw die(String.format("expected %s to be %s, found %s",
  176.                     exp.getName(),
  177.                     id(exp.getObjectId()),
  178.                     id(act.getObjectId())));
  179.         }

  180.         if (exp.getPeeledObjectId() != null
  181.                 && !AnyObjectId.equals(exp.getPeeledObjectId(), act.getPeeledObjectId())) {
  182.             throw die(String.format("expected %s to be %s, found %s",
  183.                     exp.getName(),
  184.                     id(exp.getPeeledObjectId()),
  185.                     id(act.getPeeledObjectId())));
  186.         }
  187.     }

  188.     @SuppressWarnings("nls")
  189.     private static String id(ObjectId id) {
  190.         return id != null ? id.name() : "<null>";
  191.     }
  192. }