VerifyReftable.java

  1. /*
  2.  * Copyright (C) 2017, Google Inc. and others
  3.  *
  4.  * This program and the accompanying materials are made available under the
  5.  * terms of the Eclipse Distribution License v. 1.0 which is available at
  6.  * https://www.eclipse.org/org/documents/edl-v10.php.
  7.  *
  8.  * SPDX-License-Identifier: BSD-3-Clause
  9.  */

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

  11. import java.io.FileInputStream;
  12. import java.io.IOException;
  13. import java.util.ArrayList;
  14. import java.util.Collections;
  15. import java.util.HashMap;
  16. import java.util.List;
  17. import java.util.Map;
  18. import java.util.Random;

  19. import org.eclipse.jgit.internal.storage.io.BlockSource;
  20. import org.eclipse.jgit.internal.storage.reftable.RefCursor;
  21. import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
  22. import org.eclipse.jgit.lib.AnyObjectId;
  23. import org.eclipse.jgit.lib.ObjectId;
  24. import org.eclipse.jgit.lib.Ref;
  25. import org.eclipse.jgit.lib.RefComparator;
  26. import org.eclipse.jgit.lib.TextProgressMonitor;
  27. import org.eclipse.jgit.pgm.Command;
  28. import org.eclipse.jgit.pgm.TextBuiltin;
  29. import org.kohsuke.args4j.Argument;

  30. @Command
  31. class VerifyReftable extends TextBuiltin {
  32.     private static final long SEED1 = 0xaba8bb4de4caf86cL;
  33.     private static final long SEED2 = 0x28bb5c25ad43ecb5L;

  34.     @Argument(index = 0)
  35.     private String lsRemotePath;

  36.     @Argument(index = 1)
  37.     private String reftablePath;

  38.     /** {@inheritDoc} */
  39.     @Override
  40.     protected void run() throws Exception {
  41.         List<Ref> refs = WriteReftable.readRefs(lsRemotePath);

  42.         try (FileInputStream in = new FileInputStream(reftablePath);
  43.                 BlockSource src = BlockSource.from(in);
  44.                 ReftableReader reader = new ReftableReader(src)) {
  45.             scan(refs, reader);
  46.             seek(refs, reader);
  47.             byId(refs, reader);
  48.         }
  49.     }

  50.     @SuppressWarnings("nls")
  51.     private void scan(List<Ref> refs, ReftableReader reader)
  52.             throws IOException {
  53.         errw.print(String.format("%-20s", "sequential scan..."));
  54.         errw.flush();
  55.         try (RefCursor rc = reader.allRefs()) {
  56.             for (Ref exp : refs) {
  57.                 verify(exp, rc);
  58.             }
  59.             if (rc.next()) {
  60.                 throw die("expected end of table");
  61.             }
  62.         }
  63.         errw.println(" OK");
  64.     }

  65.     @SuppressWarnings("nls")
  66.     private void seek(List<Ref> refs, ReftableReader reader)
  67.             throws IOException {
  68.         List<Ref> rnd = new ArrayList<>(refs);
  69.         Collections.shuffle(rnd, new Random(SEED1));

  70.         TextProgressMonitor pm = new TextProgressMonitor(errw);
  71.         pm.beginTask("random seek", rnd.size());
  72.         for (Ref exp : rnd) {
  73.             try (RefCursor rc = reader.seekRef(exp.getName())) {
  74.                 verify(exp, rc);
  75.                 if (rc.next()) {
  76.                     throw die("should not have ref after " + exp.getName());
  77.                 }
  78.             }
  79.             pm.update(1);
  80.         }
  81.         pm.endTask();
  82.     }

  83.     @SuppressWarnings("nls")
  84.     private void byId(List<Ref> refs, ReftableReader reader)
  85.             throws IOException {
  86.         Map<ObjectId, List<Ref>> want = groupById(refs);
  87.         List<List<Ref>> rnd = new ArrayList<>(want.values());
  88.         Collections.shuffle(rnd, new Random(SEED2));

  89.         TextProgressMonitor pm = new TextProgressMonitor(errw);
  90.         pm.beginTask("byObjectId", rnd.size());
  91.         for (List<Ref> exp : rnd) {
  92.             Collections.sort(exp, RefComparator.INSTANCE);
  93.             ObjectId id = exp.get(0).getObjectId();
  94.             try (RefCursor rc = reader.byObjectId(id)) {
  95.                 for (Ref r : exp) {
  96.                     verify(r, rc);
  97.                 }
  98.             }
  99.             pm.update(1);
  100.         }
  101.         pm.endTask();
  102.     }

  103.     private static Map<ObjectId, List<Ref>> groupById(List<Ref> refs) {
  104.         Map<ObjectId, List<Ref>> m = new HashMap<>();
  105.         for (Ref r : refs) {
  106.             ObjectId id = r.getObjectId();
  107.             if (id != null) {
  108.                 List<Ref> c = m.get(id);
  109.                 if (c == null) {
  110.                     c = new ArrayList<>(2);
  111.                     m.put(id, c);
  112.                 }
  113.                 c.add(r);
  114.             }
  115.         }
  116.         return m;
  117.     }

  118.     @SuppressWarnings("nls")
  119.     private void verify(Ref exp, RefCursor rc) throws IOException {
  120.         if (!rc.next()) {
  121.             throw die("ended before " + exp.getName());
  122.         }

  123.         Ref act = rc.getRef();
  124.         if (!exp.getName().equals(act.getName())) {
  125.             throw die(String.format("expected %s, found %s",
  126.                     exp.getName(),
  127.                     act.getName()));
  128.         }

  129.         if (exp.isSymbolic()) {
  130.             if (!act.isSymbolic()) {
  131.                 throw die("expected " + act.getName() + " to be symbolic");
  132.             }
  133.             if (!exp.getTarget().getName().equals(act.getTarget().getName())) {
  134.                 throw die(String.format("expected %s to be %s, found %s",
  135.                         exp.getName(),
  136.                         exp.getLeaf().getName(),
  137.                         act.getLeaf().getName()));
  138.             }
  139.             return;
  140.         }

  141.         if (!AnyObjectId.isEqual(exp.getObjectId(), act.getObjectId())) {
  142.             throw die(String.format("expected %s to be %s, found %s",
  143.                     exp.getName(),
  144.                     id(exp.getObjectId()),
  145.                     id(act.getObjectId())));
  146.         }

  147.         if (exp.getPeeledObjectId() != null
  148.                 && !AnyObjectId.isEqual(exp.getPeeledObjectId(),
  149.                         act.getPeeledObjectId())) {
  150.             throw die(String.format("expected %s to be %s, found %s",
  151.                     exp.getName(),
  152.                     id(exp.getPeeledObjectId()),
  153.                     id(act.getPeeledObjectId())));
  154.         }
  155.     }

  156.     @SuppressWarnings("nls")
  157.     private static String id(ObjectId id) {
  158.         return id != null ? id.name() : "<null>";
  159.     }
  160. }