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