Mercurial > hg4j
diff test/org/tmatesoft/hg/test/TestRevlog.java @ 583:47dfa0ec7e35
Effective revlog patching
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Wed, 24 Apr 2013 15:39:53 +0200 |
parents | |
children | ed243b668502 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/org/tmatesoft/hg/test/TestRevlog.java Wed Apr 24 15:39:53 2013 +0200 @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2013 TMate Software Ltd + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * For information on how to redistribute this software under + * the terms of a license other than GNU General Public License + * contact TMate Software at support@hg4j.com + */ +package org.tmatesoft.hg.test; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Arrays; + +import org.tmatesoft.hg.core.HgRepositoryNotFoundException; +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.internal.ByteArrayDataAccess; +import org.tmatesoft.hg.internal.DiffHelper; +import org.tmatesoft.hg.internal.Patch; +import org.tmatesoft.hg.internal.DiffHelper.LineSequence; +import org.tmatesoft.hg.internal.RevlogDump.RevlogReader; +import org.tmatesoft.hg.repo.HgLookup; +import org.tmatesoft.hg.repo.HgManifest; +import org.tmatesoft.hg.repo.HgManifest.Flags; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.Path; + +/** + * Not a real JUnit test now + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class TestRevlog { + private ByteBuffer patchData; + + public static void main(String[] args) throws Exception { + File indexFile = new File("/home/artem/hg/cpython/.hg/store/00manifest.i"); + new TestRevlog().run(indexFile); + } + + private void run(File indexFile) throws Exception { + final boolean shallDumpDiff = Boolean.TRUE.booleanValue(); + final boolean thoroughCheck = Boolean.TRUE.booleanValue(); + // + RevlogReader rr = new RevlogReader(indexFile); + rr.init(true); + rr.needData(true); + int startEntryIndex = 76507; // 150--87 + rr.startFrom(startEntryIndex); + rr.readNext(); + ByteBuffer baseRevision = null; + if (rr.isPatch()) { + byte[] cc = getRevisionTrueContent(indexFile.getParentFile(), rr.entryIndex, rr.linkRevision); + baseRevision = ByteBuffer.wrap(cc); + } else { + baseRevision = ByteBuffer.allocate(rr.getDataLength()); + rr.getData(baseRevision); + baseRevision.flip(); + } + ByteArrayDataAccess baseRevisionContent = new ByteArrayDataAccess(baseRevision.array(), baseRevision.arrayOffset(), baseRevision.remaining()); + // + final long start = System.currentTimeMillis(); + int n = 1419; + Patch seqPatch = null, normalizedPatch = null; + while (rr.hasMore() && n-- > 0) { + rr.readNext(); + if (!rr.isPatch()) { + break; + } + if (rr.getDataLength() == 0) { + System.out.printf("Empty content of revision %d\n", rr.entryIndex); + continue; + } + Patch p1 = createPatch(rr); + if (seqPatch != null) { + if (n < 1) { + System.out.println("+" + p1); + System.currentTimeMillis(); + } + seqPatch = seqPatch.apply(p1); + Patch ppp = normalizedPatch.apply(p1); + normalizedPatch = ppp.normalize(); + if (n <= 1) { + System.out.println("=" + seqPatch); + } + if (n == 0) { + System.out.println("A" + ppp); + System.out.println("N" + normalizedPatch); + normalizedPatch = ppp; + } + // + if (!thoroughCheck) { + if (baseRevisionContent.length() + seqPatch.patchSizeDelta() != rr.actualLen) { + System.out.printf("Sequential patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); + } + if (baseRevisionContent.length() + normalizedPatch.patchSizeDelta() != rr.actualLen) { + System.out.printf("Normalized patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); + } + } else { + byte[] origin = getRevisionTrueContent(indexFile.getParentFile(), rr.entryIndex, rr.linkRevision); + try { + byte[] result1 = seqPatch.apply(baseRevisionContent, rr.actualLen); + if (!Arrays.equals(result1, origin)) { + System.out.printf("Sequential patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); + } + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.printf("Failure at entry %d (+%d)\n", rr.entryIndex, rr.entryIndex - startEntryIndex); + ex.printStackTrace(); + } + try { + byte[] result2 = normalizedPatch.apply(baseRevisionContent, rr.actualLen); + if (!Arrays.equals(result2, origin)) { + System.out.printf("Normalized patches:\tPatchRevision #%d (+%d, cset:%d) failed\n", rr.entryIndex, rr.entryIndex - startEntryIndex, rr.linkRevision); + } + } catch (ArrayIndexOutOfBoundsException ex) { + System.err.printf("Failure at entry %d (+%d)\n", rr.entryIndex, rr.entryIndex - startEntryIndex); + ex.printStackTrace(); + } + } + } else { + seqPatch = p1; + normalizedPatch = p1.normalize(); + } + } + final long end1 = System.currentTimeMillis(); + // +// byte[] result = seqPatch.apply(baseRevisionContent, rr.actualLen); + byte[] result = normalizedPatch.apply(baseRevisionContent, rr.actualLen); + final long end2 = System.currentTimeMillis(); + byte[] origin = getRevisionTrueContent(indexFile.getParentFile(), rr.entryIndex, rr.linkRevision); + final long end3 = System.currentTimeMillis(); + rr.done(); + System.out.printf("Collected patches up to revision %d. Patches total: %d, last contains %d elements\n", rr.entryIndex, rr.entryIndex - startEntryIndex + 1, seqPatch.count()); + if (!Arrays.equals(result, origin)) { + if (shallDumpDiff) { + diff(result, origin); + dumpLineDifference(result, origin); + } else { + System.out.println("FAILURE!"); + } + } else { + System.out.println("OK!"); + System.out.printf("Iterate: %d ms, apply collected: %d ms, total=%d ms; Conventional: %d ms\n", (end1-start), (end2-end1), (end2-start), (end3-end2)); + } + Patch normalized = normalizedPatch; //seqPatch.normalize(); + System.out.printf("N%s\n%d => %d patch elements\n", normalized, seqPatch.count(), normalized.count()); +// System.out.println(rs); + } + + private void dumpLineDifference(byte[] result, byte[] origin) { + String rs = new String(result).replace('\0', '\t'); + String os = new String(origin).replace('\0', '\t'); + String[] rsLines = rs.split("\n"); + String[] osLines = os.split("\n"); + int approxPos = 0; + for (int i = 0; i < Math.min(rsLines.length, osLines.length); i++) { + if (!rsLines[i].equals(osLines[i])) { + System.out.printf("@%d (offset ~%d)\n\t%s\n\t%s\n", i, approxPos, osLines[i], rsLines[i]); + } + approxPos += rsLines[i].length() + 1; + } + } + + private void diff(byte[] result, byte[] origin) { + DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>(); + pg.init(LineSequence.newlines(origin), LineSequence.newlines(result)); + pg.findMatchingBlocks(new DiffHelper.DeltaDumpInspector<LineSequence>()); + } + + private Patch createPatch(RevlogReader rr) throws IOException { + assert rr.isPatch(); + if (patchData == null || patchData.capacity() < rr.getDataLength()) { + patchData = ByteBuffer.allocate(rr.getDataLength()); + } else { + patchData.clear(); + } + rr.getData(patchData); + patchData.flip(); + Patch patch1 = new Patch(); + patch1.read(new ByteArrayDataAccess(patchData.array(), patchData.arrayOffset(), patchData.remaining())); + return patch1; + } + + private byte[] getRevisionTrueContent(File repoLoc, final int manifestRev, int clogRev) throws HgRepositoryNotFoundException { + HgRepository hgRepo = new HgLookup().detect(repoLoc); + final ByteArrayOutputStream out = new ByteArrayOutputStream(1024 * 1000); + hgRepo.getManifest().walk(clogRev, clogRev, new HgManifest.Inspector() { + + public boolean next(Nodeid nid, Path fname, Flags flags) { + try { + out.write(fname.toString().getBytes()); + out.write(0); + out.write(nid.toString().getBytes()); + if (flags == Flags.Exec) { + out.write('x'); + } else if (flags == Flags.Link) { + out.write('l'); + } + out.write('\n'); + } catch (IOException e) { + throw new IllegalStateException(e); + } + return true; + } + + public boolean end(int manifestRevisionIndex) { + return false; + } + + public boolean begin(int manifestRevisionIndex, Nodeid manifestRevision, int changelogRevisionIndex) { + if (manifestRev != manifestRevisionIndex) { + throw new IllegalStateException(String.valueOf(manifestRevisionIndex)); + } + return true; + } + }); + return out.toByteArray(); + } +}