253 lines
5.6 KiB
D
253 lines
5.6 KiB
D
module libxdiff.unittests;
|
|
|
|
import std.algorithm : equal;
|
|
import std.array : appender;
|
|
import std.exception : assertThrown;
|
|
import std.range : iota, cycle, take;
|
|
import libxdiff.mmfile : MMFile;
|
|
import libxdiff.mmblocks : MMBlocks;
|
|
|
|
ubyte[] genCycled(size_t n)
|
|
{
|
|
auto acc = appender!(ubyte[])();
|
|
foreach (v; iota(0, 240).cycle.take(n))
|
|
acc.put(cast(ubyte) v);
|
|
return acc.data;
|
|
}
|
|
|
|
ubyte[] blocksToBytes(const MMBlocks b)
|
|
{
|
|
auto tmp = b.clone();
|
|
auto mf = MMFile.fromBlocksMoved(tmp);
|
|
return (cast(const(ubyte)[]) mf.asSlice()).dup;
|
|
}
|
|
|
|
bool sameBytes(const MMFile a, const MMFile b)
|
|
{
|
|
return a.asSlice().equal(b.asSlice());
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto f = new MMFile;
|
|
assert(f.size() == 0);
|
|
assert(f.isCompact());
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto data = cast(ubyte[]) "hello world";
|
|
auto f = MMFile.fromBytes(data);
|
|
assert(f.size() == data.length);
|
|
assert(f.isCompact());
|
|
assert(f.asSlice().equal(data));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto data = genCycled(15_000);
|
|
auto f = MMFile.fromBytes(data);
|
|
assert(f.size() == data.length);
|
|
assert(f.isCompact());
|
|
assert(f.asSlice().equal(data));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto data = genCycled(15_000);
|
|
auto f = MMFile.fromBytes(data);
|
|
auto f2 = MMFile.fromBytes(f.asSlice().dup);
|
|
assert(sameBytes(f, f2));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto data = genCycled(15_000);
|
|
auto b = MMBlocks.fromBytes(data);
|
|
auto b2 = b.clone();
|
|
auto bytes = blocksToBytes(b);
|
|
auto bytes2 = blocksToBytes(b2);
|
|
assert(bytes.equal(bytes2));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto data = genCycled(15_000);
|
|
auto f = MMFile.fromBytes(data);
|
|
auto f2 = MMFile.fromBytes(data.dup);
|
|
assert(sameBytes(f, f2));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert((new MMFile).asSlice().length == 0);
|
|
|
|
auto data = genCycled(15_000);
|
|
auto f = MMFile.fromBytes(data);
|
|
assert(f.asSlice().equal(data));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto empty = new MMFile;
|
|
assert(empty.asSlice().length == 0);
|
|
|
|
auto data = genCycled(15_000);
|
|
auto f = MMFile.fromBytes(data);
|
|
assert(f.asSlice()[0] == data[0]);
|
|
|
|
auto mut = f.asSliceMut();
|
|
mut[0] = cast(ubyte)(mut[0] + 1);
|
|
assert(f.asSlice()[0] == data[0] + 1);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto data = cast(ubyte[]) "hello world\n";
|
|
auto data2 = cast(ubyte[]) "hello world!\n";
|
|
auto f = MMFile.fromBytes(data);
|
|
auto f2 = MMFile.fromBytes(data2);
|
|
|
|
auto patch = f.computePatch(f2);
|
|
auto r = f.applyPatch(patch);
|
|
assert(r.success);
|
|
assert(r.patched.asSlice().equal(data2));
|
|
assert(r.rejected.asSlice().length == 0);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto f = MMFile.fromBytes(cast(ubyte[]) "hello world\n");
|
|
auto f2 = MMFile.fromBytes(cast(ubyte[]) "hello world!\n");
|
|
|
|
auto p1 = f.computePatch(f2);
|
|
auto r1 = f.applyPatch(p1);
|
|
assert(r1.success);
|
|
assert(r1.patched.asSlice().equal(f2.asSlice()));
|
|
|
|
auto m = f2.asSliceMut();
|
|
m[0] = cast(ubyte) 'j';
|
|
|
|
auto p2 = f.computePatch(f2);
|
|
auto r2 = f.applyPatch(p2);
|
|
assert(r2.success);
|
|
assert(r2.patched.asSlice().equal(f2.asSlice()));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto base = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world\n");
|
|
auto a = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world changed\n");
|
|
auto b = MMFile.fromBytes(cast(ubyte[]) "header_changed\nline2\nline3\nline4\nhello world\n");
|
|
|
|
auto lines = appender!(string[])();
|
|
auto rej = appender!(string[])();
|
|
|
|
int acceptCb(const(ubyte)[] s) nothrow
|
|
{
|
|
lines.put(cast(string) s.idup);
|
|
return 0;
|
|
}
|
|
|
|
int rejectCb(const(ubyte)[] s) nothrow
|
|
{
|
|
rej.put(cast(string) s.idup);
|
|
return 0;
|
|
}
|
|
|
|
base.merge3Raw(a, b, &acceptCb, &rejectCb);
|
|
|
|
auto expected = [
|
|
"header_changed\n",
|
|
"line2\n",
|
|
"line3\n",
|
|
"line4\n",
|
|
"hello world changed\n",
|
|
];
|
|
assert(lines.data.equal(expected));
|
|
assert(rej.data.length == 0);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto base = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world\n");
|
|
auto a = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world changed\n");
|
|
auto b = MMFile.fromBytes(
|
|
cast(ubyte[]) "header\nline2\nline3\nline4\nhello world also changed\n");
|
|
|
|
auto lines = appender!(string[])();
|
|
auto rej = appender!(string[])();
|
|
|
|
int acceptCb(const(ubyte)[] s) nothrow
|
|
{
|
|
lines.put(cast(string) s.idup);
|
|
return 0;
|
|
}
|
|
|
|
int rejectCb(const(ubyte)[] s) nothrow
|
|
{
|
|
rej.put(cast(string) s.idup);
|
|
return 0;
|
|
}
|
|
|
|
base.merge3Raw(a, b, &acceptCb, &rejectCb);
|
|
|
|
assert(lines.data.length > 0);
|
|
assert(rej.data.length > 0);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto base = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world\n");
|
|
auto a = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world changed\n");
|
|
auto b = MMFile.fromBytes(cast(ubyte[]) "header_changed\nline2\nline3\nline4\nhello world\n");
|
|
|
|
auto lines = appender!(string[])();
|
|
|
|
int acceptCb(const(ubyte)[] s) nothrow
|
|
{
|
|
static size_t cnt = 0;
|
|
++cnt;
|
|
if (cnt > 3)
|
|
return -1;
|
|
lines.put(cast(string) s.idup);
|
|
return 0;
|
|
}
|
|
|
|
int rejectCb(const(ubyte)[] s) nothrow
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
assertThrown!Exception(base.merge3Raw(a, b, &acceptCb, &rejectCb));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto base = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world\n");
|
|
auto want = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world changed\n");
|
|
|
|
auto patch = base.computePatch(want);
|
|
auto res = base.applyPatch(patch);
|
|
|
|
assert(res.success);
|
|
assert(sameBytes(res.patched, want));
|
|
assert(res.rejected.asSlice().length == 0);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
auto base = MMFile.fromBytes(cast(ubyte[]) "header\nline2\nline3\nline4\nhello world\n");
|
|
auto want = MMFile.fromBytes(
|
|
cast(ubyte[]) "header changed\nline2\nline3\nline4\nhello world changed\n");
|
|
|
|
auto patch = base.computePatch(want);
|
|
|
|
auto m = base.asSliceMut();
|
|
m[0] = cast(ubyte) 'b';
|
|
|
|
auto res = base.applyPatch(patch);
|
|
|
|
assert(!res.success);
|
|
assert(res.rejected.asSlice().length > 0);
|
|
}
|