From afd6c698ade77838293ccc5651295fdbf02fd51c Mon Sep 17 00:00:00 2001 From: Algent Date: Thu, 3 Sep 2026 22:32:20 +0200 Subject: [PATCH] test: characterize post microblock trait --- .../PostMicroblockTraitsFunctionalTest.java | 174 ++++++++++++++ src/test/fixtures/README.md | 17 ++ .../compat/ReferenceScalaPostMicroblock.scala | 55 +++++ ...tMicroblockTraitsCharacterizationTest.java | 223 ++++++++++++++++++ .../compat/ReferencePostFace.class.b64 | 1 + .../ReferencePostMicroblockBase.class.b64 | 1 + .../ReferenceScalaPostMicroblock.class.b64 | 1 + 7 files changed, 472 insertions(+) create mode 100644 src/functionalTest/java/codechicken/multipart/test/PostMicroblockTraitsFunctionalTest.java create mode 100644 src/test/fixtures/scala/codechicken/multipart/compat/ReferenceScalaPostMicroblock.scala create mode 100644 src/test/java/codechicken/microblock/PostMicroblockTraitsCharacterizationTest.java create mode 100644 src/test/resources/compat/ReferencePostFace.class.b64 create mode 100644 src/test/resources/compat/ReferencePostMicroblockBase.class.b64 create mode 100644 src/test/resources/compat/ReferenceScalaPostMicroblock.class.b64 diff --git a/src/functionalTest/java/codechicken/multipart/test/PostMicroblockTraitsFunctionalTest.java b/src/functionalTest/java/codechicken/multipart/test/PostMicroblockTraitsFunctionalTest.java new file mode 100644 index 0000000..80588aa --- /dev/null +++ b/src/functionalTest/java/codechicken/multipart/test/PostMicroblockTraitsFunctionalTest.java @@ -0,0 +1,174 @@ +package codechicken.multipart.test; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.concurrent.atomic.AtomicInteger; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import org.junit.jupiter.api.Test; + +import codechicken.lib.data.MCDataInput; +import codechicken.lib.data.MCDataOutputWrapper; +import codechicken.lib.vec.Cuboid6; +import codechicken.microblock.EdgeMicroClass; +import codechicken.microblock.FaceMicroClass$; +import codechicken.microblock.MicroMaterialRegistry; +import codechicken.microblock.Microblock; +import codechicken.microblock.MicroblockGenerator; +import codechicken.microblock.PostMicroClass; +import codechicken.microblock.PostMicroClass$; +import codechicken.microblock.PostMicroblock; + +class PostMicroblockTraitsFunctionalTest { + + @Test + void generatedPostsKeepAllNineBoundsTorchSupportAndEdgeItems() { + Microblock part = createPost(); + PostMicroblock post = (PostMicroblock) part; + assertSame(PostMicroClass$.MODULE$, post.microClass()); + assertSame(PostMicroClass$.MODULE$, part.microClass()); + assertEquals("mcr_post", part.getType()); + assertEquals(0.5F, post.getResistanceFactor()); + assertFalse(post.allowCompleteOcclusion()); + int[] longAxes = { 1, 2, 0 }; + for (int size : new int[] { 2, 4, 6 }) { + for (int axis = 0; axis < 3; axis++) { + part.setShape(size, axis); + assertEquals(size, part.getSize()); + assertEquals(axis, part.getShape()); + assertEquals(axis == 0, part.canPlaceTorchOnTop()); + assertEquals(EdgeMicroClass.getClassId(), part.itemClassID()); + Cuboid6 bounds = part.getBounds(); + assertSame(PostMicroClass.aBounds()[size * 16 + axis], bounds); + double[] min = { bounds.min.x, bounds.min.y, bounds.min.z }; + double[] max = { bounds.max.x, bounds.max.y, bounds.max.z }; + for (int coordinate = 0; coordinate < 3; coordinate++) { + assertEquals(coordinate == longAxes[axis] ? 0 : 0.5 - size / 16D, min[coordinate], 1e-12); + assertEquals(coordinate == longAxes[axis] ? 1 : 0.5 + size / 16D, max[coordinate], 1e-12); + } + assertSame(bounds, post.getOcclusionBoxes().get(0)); + assertSame(bounds, post.getPartialOcclusionBoxes().get(0)); + int volume = 0; + for (ItemStack drop : part.getDrops()) { + assertEquals(EdgeMicroClass.getClassId(), drop.getItemDamage() >> 8); + volume += (drop.getItemDamage() & 255) * drop.stackSize; + } + assertEquals(size, volume); + ItemStack pick = part.pickItem(null); + assertEquals(EdgeMicroClass.getClassId(), pick.getItemDamage() >> 8); + assertEquals(size == 4 ? 4 : 2, pick.getItemDamage() & 255); + } + } + } + + @Test + void boundsKeepSignedIndexFailuresAndLiveArrayReplacement() { + Microblock part = createPost(); + for (int shape : new int[] { 0, 16, 35, 127 }) { + part.shape_$eq((byte) shape); + assertNull(part.getBounds()); + } + for (int shape = -128; shape < 0; shape++) { + part.shape_$eq((byte) shape); + assertThrows(ArrayIndexOutOfBoundsException.class, part::getBounds); + } + Cuboid6[] original = PostMicroClass.aBounds(); + Cuboid6[] replacement = new Cuboid6[256]; + Cuboid6 sentinel = new Cuboid6(0.1, 0.2, 0.3, 0.4, 0.5, 0.6); + replacement[65] = sentinel; + part.setShape(4, 1); + try { + PostMicroClass.aBounds_$eq(replacement); + assertSame(sentinel, part.getBounds()); + replacement[65] = null; + assertNull(part.getBounds()); + PostMicroClass.aBounds_$eq(new Cuboid6[1]); + assertThrows(ArrayIndexOutOfBoundsException.class, part::getBounds); + PostMicroClass.aBounds_$eq(null); + assertThrows(NullPointerException.class, part::getBounds); + } finally { + PostMicroClass.aBounds_$eq(original); + } + assertSame(original[65], part.getBounds()); + } + + @Test + void generatedPostsKeepPackedAxisAndMaterialInNbtAndDescriptions() { + Microblock part = createPost(); + Microblock loaded = createPost(); + ByteArrayOutputStream prefixBytes = new ByteArrayOutputStream(); + MicroMaterialRegistry.writeMaterialID(new MCDataOutputWrapper(new DataOutputStream(prefixBytes)), 0); + byte[] prefix = prefixBytes.toByteArray(); + for (int size : new int[] { 2, 4, 6 }) { + for (int axis = 0; axis < 3; axis++) { + byte shape = (byte) (size * 16 + axis); + part.setShape(size, axis); + NBTTagCompound tag = new NBTTagCompound(); + part.save(tag); + assertEquals(shape, tag.getByte("shape")); + assertEquals(MicroMaterialRegistry.materialName(0), tag.getString("material")); + loaded.load(tag); + assertEquals(shape, loaded.shape()); + assertEquals(0, loaded.material()); + assertSame(part.getBounds(), loaded.getBounds()); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + part.writeDesc(new MCDataOutputWrapper(new DataOutputStream(bytes))); + byte[] expected = Arrays.copyOf(prefix, prefix.length + 1); + expected[prefix.length] = shape; + assertArrayEquals(expected, bytes.toByteArray()); + AtomicInteger reads = new AtomicInteger(); + MCDataInput input = (MCDataInput) Proxy.newProxyInstance( + getClass().getClassLoader(), + new Class[] { MCDataInput.class }, + (proxy, method, args) -> { + assertEquals("readByte", method.getName()); + reads.incrementAndGet(); + return shape; + }); + loaded.setShape(4, 0); + loaded.readDesc(input); + assertEquals(1, reads.get()); + assertEquals(shape, loaded.shape()); + assertSame(part.getBounds(), loaded.getBounds()); + } + } + } + + @Test + void generatedOcclusionAllowsCrossingPostsAndOnlyAlignedOrNonOverlappingCovers() { + Microblock part = createPost(); + Microblock other = createPost(); + Microblock face = MicroblockGenerator.create(FaceMicroClass$.MODULE$, 0, false); + for (int size : new int[] { 2, 4, 6 }) { + for (int axis = 0; axis < 3; axis++) { + part.setShape(size, axis); + for (int otherSize : new int[] { 2, 4, 6 }) { + for (int otherAxis = 0; otherAxis < 3; otherAxis++) { + other.setShape(otherSize, otherAxis); + assertEquals(axis != otherAxis, part.occlusionTest(other)); + } + } + for (int coverSize = 1; coverSize <= 7; coverSize++) { + for (int side = 0; side < 6; side++) { + face.setShape(coverSize, side); + boolean expected = side / 2 == axis || coverSize / 8D <= 0.5 - size / 16D; + assertEquals( + expected, + part.occlusionTest(face), + "size=" + size + ", axis=" + axis + ", cover=" + coverSize + ", side=" + side); + } + } + } + } + } + + private static Microblock createPost() { + return MicroblockGenerator.create(PostMicroClass$.MODULE$, 0, false); + } +} diff --git a/src/test/fixtures/README.md b/src/test/fixtures/README.md index e8300ad..43a8f33 100644 --- a/src/test/fixtures/README.md +++ b/src/test/fixtures/README.md @@ -216,3 +216,20 @@ Compiled with Scala 2.11.5 under Java 8, `-target:jvm-1.8`, against the unchange `6524a023b8015e6ecc75aaab9dafa09bb353e66ccdb72e7d76a3469288ba5952`; class-file SHA-256 is `2b51d17fb89999fd0324e4ea53843596539c5885c2b2eb0a476926e18446b587`, stored as `src/test/resources/compat/ReferenceScalaEdgeMicroblock.class.b64`. Do not regenerate it against the port. + +### `ReferenceScalaPostMicroblock` + +`scala/codechicken/multipart/compat/ReferenceScalaPostMicroblock.scala` freezes post forwarders with a real superclass +predecessor and a face candidate. Tests observe special-case ordering, the synthetic super accessor, virtual shape, +bounds and list dispatch, list mutation failures, and malformed-candidate errors. + +Compiled with Scala 2.11.5 under Java 8, `-target:jvm-1.8`, against the unchanged dev jar at `7c4c87e` +(SHA-256 `c9fcc62deebf198f3aa1de1799d3a58a31cae9fa92f1b5dd129a84b93eaab73d`). Source SHA-256 is +`d22d82ab5e53074022d305bb9537a5bae77804cdc28b0bf46f58686f47e2b62d`. Frozen class files under +`src/test/resources/compat/` have these SHA-256s; do not regenerate them against the port: + +| Class | SHA-256 | +| --- | --- | +| `ReferencePostMicroblockBase` | `a6bed1c01fd74be061edfa287b724679c4cdbcb9c550cc3a3304ebda6973c7ac` | +| `ReferenceScalaPostMicroblock` | `ceafadad14da1f749ec60af6609d1b2eb7afa0666425fff25271dee1e46a7bc2` | +| `ReferencePostFace` | `066037dc6000fbcf2404583ff4ba8a9048b140beb0e1c27700564284bc4d8c06` | diff --git a/src/test/fixtures/scala/codechicken/multipart/compat/ReferenceScalaPostMicroblock.scala b/src/test/fixtures/scala/codechicken/multipart/compat/ReferenceScalaPostMicroblock.scala new file mode 100644 index 0000000..9acc25c --- /dev/null +++ b/src/test/fixtures/scala/codechicken/multipart/compat/ReferenceScalaPostMicroblock.scala @@ -0,0 +1,55 @@ +package codechicken.multipart.compat + +import codechicken.lib.vec.Cuboid6 +import codechicken.microblock.{CommonMicroblock, CommonMicroClass, Microblock, PostMicroblock} +import codechicken.multipart.TMultiPart + +/** A real superclass predecessor for the frozen PostMicroblock super accessor. */ +abstract class ReferencePostMicroblockBase extends Microblock(31) { + var events: java.util.List[String] = new java.util.ArrayList[String]() + var label = "self" + var superResult = true + var superFailure = false + + override def occlusionTest(part: TMultiPart): Boolean = { + events.add("super:" + label) + if (superFailure) throw new IllegalStateException("super failure") + superResult + } +} + +/** Frozen trait forwarders with observable virtual calls and an actual super chain. */ +class ReferenceScalaPostMicroblock extends ReferencePostMicroblockBase with PostMicroblock { + var selectedShape = 0 + var selectedBounds: Cuboid6 = Cuboid6.full.copy + var replacementBoxes: java.util.List[Cuboid6] = null + + override def getShape = { + events.add("shape:" + label) + selectedShape + } + + override def getBounds = { + events.add("bounds:" + label) + selectedBounds + } + + override def getType = { + events.add("type:" + label) + "mcr_face" + } + + override def getOcclusionBoxes: java.util.List[Cuboid6] = { + events.add("boxes:" + label) + if (replacementBoxes == null) super.getOcclusionBoxes else replacementBoxes + } +} + +class ReferencePostFace extends Microblock(0) with CommonMicroblock { + var events: java.util.List[String] = new java.util.ArrayList[String]() + var slot = 0 + def microClass: CommonMicroClass = null + override def getType = { events.add("type:face"); "mcr_face" } + override def getSlot = { events.add("slot:face"); slot } + def getBounds = { events.add("bounds:face"); Cuboid6.full } +} diff --git a/src/test/java/codechicken/microblock/PostMicroblockTraitsCharacterizationTest.java b/src/test/java/codechicken/microblock/PostMicroblockTraitsCharacterizationTest.java new file mode 100644 index 0000000..1b4ce51 --- /dev/null +++ b/src/test/java/codechicken/microblock/PostMicroblockTraitsCharacterizationTest.java @@ -0,0 +1,223 @@ +package codechicken.microblock; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Scanner; + +import org.junit.jupiter.api.Test; + +import codechicken.lib.vec.Cuboid6; +import codechicken.multipart.JNormalOcclusion; +import codechicken.multipart.JPartialOcclusion; +import codechicken.multipart.TMultiPart; + +/** Executes frozen Scala forwarders, including the real predecessor of PostMicroblock's super accessor. */ +class PostMicroblockTraitsCharacterizationTest { + + @Test + void occlusionListsAreFreshReadOnlySnapshotsWithVirtualBoundsAndPartialDispatch() throws Exception { + Fixture f = new Fixture(); + Cuboid6 bounds = new Cuboid6(0, 0, 0, 0.5, 1, 1); + f.set(f.part, "selectedBounds", Cuboid6.class, bounds); + List first = f.post.getOcclusionBoxes(); + assertEquals(Arrays.asList("boxes:self", "bounds:self"), f.events); + assertSame(bounds, first.get(0)); + assertEquals(1, first.size()); + assertThrows(UnsupportedOperationException.class, () -> first.add(bounds)); + assertThrows(UnsupportedOperationException.class, () -> first.set(0, bounds)); + assertThrows(UnsupportedOperationException.class, () -> first.remove(0)); + List second = f.post.getOcclusionBoxes(); + assertNotSame(first, second); + f.set(f.part, "selectedBounds", Cuboid6.class, null); + assertNull(f.post.getOcclusionBoxes().get(0)); + assertSame(bounds, first.get(0)); + bounds.max.x = 0.75; + assertEquals(0.75, first.get(0).max.x); + + List replacement = new ArrayList<>(); + f.set(f.part, "replacementBoxes", List.class, replacement); + f.events.clear(); + assertSame(replacement, f.post.getPartialOcclusionBoxes()); + assertEquals(Collections.singletonList("boxes:self"), f.events); + } + + @Test + void torchSupportUsesTheVirtualShapeOnceWithoutMaskingIt() throws Exception { + Fixture f = new Fixture(); + for (int shape : new int[] { Integer.MIN_VALUE, -1, 0, 1, 2, 16, Integer.MAX_VALUE }) { + f.set(f.part, "selectedShape", int.class, shape); + f.events.clear(); + assertEquals(shape == 0, f.post.canPlaceTorchOnTop()); + assertEquals(Collections.singletonList("shape:self"), f.events); + } + } + + @Test + void postsCompareOtherShapeFirstAndBypassTypeBoundsAndSuper() throws Exception { + Fixture f = new Fixture(); + Microblock other = f.newPost("other"); + f.set(f.part, "superFailure", boolean.class, true); + for (int ownShape : new int[] { -1, 0, 1, 2, 16, Integer.MAX_VALUE }) { + f.set(f.part, "selectedShape", int.class, ownShape); + for (int otherShape : new int[] { -1, 0, 1, 2, 16, Integer.MAX_VALUE }) { + f.set(other, "selectedShape", int.class, otherShape); + f.events.clear(); + assertEquals(ownShape != otherShape, f.part.occlusionTest(other)); + assertEquals(Arrays.asList("shape:other", "shape:self"), f.events); + } + } + f.events.clear(); + assertFalse(f.part.occlusionTest(f.part)); + assertEquals(Arrays.asList("shape:self", "shape:self"), f.events); + } + + @Test + void alignedFacesBypassOverlapAndSuperUsingArithmeticSlotShift() throws Exception { + Fixture f = new Fixture(); + TMultiPart face = f.newFace(); + f.set(f.part, "superFailure", boolean.class, true); + for (int shape = -2; shape <= 3; shape++) { + f.set(f.part, "selectedShape", int.class, shape); + for (int slot = -4; slot < 8; slot++) { + f.set(face, "slot", int.class, slot); + f.events.clear(); + boolean aligned = Math.floorDiv(slot, 2) == shape; + assertEquals(aligned, f.part.occlusionTest(face)); + List expected = new ArrayList<>(Arrays.asList("type:face", "slot:face", "shape:self")); + if (!aligned) expected.addAll(Arrays.asList("bounds:face", "boxes:self", "bounds:self")); + assertEquals(expected, f.events); + } + } + } + + @Test + void normalAndPartialBoxesPrecedeAndShortCircuitTheRealSuperclass() throws Exception { + Fixture f = new Fixture(); + Candidate other = new Candidate(f.events, "test:other"); + Cuboid6 separate = new Cuboid6(2, 2, 2, 3, 3, 3); + for (boolean partialOverlap : new boolean[] { false, true }) { + other.normal = Collections.singletonList(partialOverlap ? separate : Cuboid6.full); + other.partial = Collections.singletonList(partialOverlap ? Cuboid6.full : separate); + f.events.clear(); + assertFalse(f.part.occlusionTest(other)); + assertEquals( + Arrays.asList("type:other", "normal:other", "partial:other", "boxes:self", "bounds:self"), + f.events); + } + other.normal = Collections.singletonList(separate); + other.partial = Collections.emptyList(); + for (boolean result : new boolean[] { false, true }) { + f.set(f.part, "superResult", boolean.class, result); + f.events.clear(); + assertEquals(result, f.part.occlusionTest(other)); + assertEquals( + Arrays.asList( + "type:other", + "normal:other", + "partial:other", + "boxes:self", + "bounds:self", + "super:self"), + f.events); + } + f.set(f.part, "superFailure", boolean.class, true); + assertEquals( + "super failure", + assertThrows(IllegalStateException.class, () -> f.part.occlusionTest(other)).getMessage()); + } + + @Test + void malformedCandidatesFailBeforeBoundsAndSuperclassDispatch() throws Exception { + Fixture f = new Fixture(); + assertThrows(NullPointerException.class, () -> f.part.occlusionTest(null)); + assertTrue(f.events.isEmpty()); + Candidate other = new Candidate(f.events, null); + assertThrows(NullPointerException.class, () -> f.part.occlusionTest(other)); + assertEquals(Collections.singletonList("type:other"), f.events); + f.events.clear(); + other.type = "mcr_face"; + assertThrows(ClassCastException.class, () -> f.part.occlusionTest(other)); + assertEquals(Collections.singletonList("type:other"), f.events); + } + + private static final class Candidate extends TMultiPart implements JNormalOcclusion, JPartialOcclusion { + + final List events; + String type; + Iterable normal = Collections.emptyList(); + Iterable partial = Collections.emptyList(); + + Candidate(List events, String type) { + this.events = events; + this.type = type; + } + + @Override + public String getType() { + events.add("type:other"); + return type; + } + + @Override + public Iterable getOcclusionBoxes() { + events.add("normal:other"); + return normal; + } + + @Override + public Iterable getPartialOcclusionBoxes() { + events.add("partial:other"); + return partial; + } + } + + private static final class Fixture extends ClassLoader { + + final List events = new ArrayList<>(); + final Microblock part; + final PostMicroblock post; + + Fixture() throws Exception { + super(Microblock.class.getClassLoader()); + for (String name : new String[] { "ReferencePostMicroblockBase", "ReferenceScalaPostMicroblock", + "ReferencePostFace" }) { + InputStream input = Objects + .requireNonNull(getClass().getResourceAsStream("/compat/" + name + ".class.b64")); + byte[] bytes; + try (Scanner scanner = new Scanner(input, StandardCharsets.US_ASCII.name()).useDelimiter("\\A")) { + bytes = Base64.getMimeDecoder().decode(scanner.next()); + } + defineClass(null, bytes, 0, bytes.length); + } + part = newPost("self"); + post = (PostMicroblock) part; + } + + Microblock newPost(String label) throws Exception { + Microblock result = (Microblock) loadClass("codechicken.multipart.compat.ReferenceScalaPostMicroblock") + .getConstructor().newInstance(); + set(result, "label", String.class, label); + set(result, "events", List.class, events); + return result; + } + + TMultiPart newFace() throws Exception { + TMultiPart result = (TMultiPart) loadClass("codechicken.multipart.compat.ReferencePostFace") + .getConstructor().newInstance(); + set(result, "events", List.class, events); + return result; + } + + void set(Object target, String name, Class type, Object value) throws Exception { + target.getClass().getMethod(name + "_$eq", type).invoke(target, value); + } + } +} diff --git a/src/test/resources/compat/ReferencePostFace.class.b64 b/src/test/resources/compat/ReferencePostFace.class.b64 new file mode 100644 index 0000000..bbfe08d --- /dev/null +++ b/src/test/resources/compat/ReferencePostFace.class.b64 @@ -0,0 +1 @@ +yv66vgAAADQAfgEALmNvZGVjaGlja2VuL211bHRpcGFydC9jb21wYXQvUmVmZXJlbmNlUG9zdEZhY2UHAAEBACFjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL01pY3JvYmxvY2sHAAMBACdjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL0NvbW1vbk1pY3JvYmxvY2sHAAUBACJSZWZlcmVuY2VTY2FsYVBvc3RNaWNyb2Jsb2NrLnNjYWxhAQAeTHNjYWxhL3JlZmxlY3QvU2NhbGFTaWduYXR1cmU7AQAFYnl0ZXMBAp0GAXkzQSEBAgETCQkiKxo0ZmUWdDctGilwZ1I0FW1ZMwsFDSERQUIycFtCDEdPAwIGDQVJUS4eN3VTQgwnDx4GAg8FWTFtHDNmRyJMN21bM28HARkyAQEGESEJWWEiRAENFQlpYSEBBm5TDhR4Thk3cEcuTCFhBAcDFTVLN01dOGNZPhw3DgUCDCMlESEDBAIRBz5sV24cOE5TDhR4Thk3cEcuRFEBBgEFAlUJYQFQNW9TUnREIwEMEQVdAVEiAQIJD2UBARkhQwE1BTFRTV4zb2lOLBJhBwkEOQUaUyJBDwsFeXkSAUI7dVMyVBEBSQEFVQY0GC0DAiM7CSFBKls6dSEJISNGBAImUTUJYUVDASgDFRk4LRk3YhMJSWMlAQRRZRYkV01aBQNXMRJhYRU7c1M6PCdCQRUnER1xAwExQQUCPQohIho8Zl1SHHgMSjNyKQkBNAcFAiZjJREhRwoCBSs6TEcPQwQ1WwUFCRlBDgIHYSQTBwMENwEBBkthRwEIS1osZw5eOiERHUEEATFBBQJlCkFhHTdwaVYJIQgFAiZ3JRFBSAoCBBM6JAhiAiABAQQlCWFQAQlnMnxHbxgTZmNSEQEHEQUIaXUKCRExATsRGREFASlRBXUFKTEPXDh1QSEpQQkBQwELBlFRLlsyc18OYxdtXToVAxkDImFDJAoFIWMhAUUicFs2fGcuVDVkZT4cRS5ZOnQRFVEFASIRTAMdOVcNHit6YRYkEgEUCQMbQmsRQRQGAx9+CUEBXDFvTyYRMUYUBQYlAiEJJU8BCE8WJDgLXDh1ERUhBgEiAVYDJTlXDR4icGs6JDcvRgFXIQk5RixEAVkVCUkmLEECd0sOVCFhFwQCBzFMJy0DAl4xCjkxKR4ycFMSNAQBAAZldmVudHMBABBMamF2YS91dGlsL0xpc3Q7AQAkTGphdmEvdXRpbC9MaXN0PExqYXZhL2xhbmcvU3RyaW5nOz47AQAEc2xvdAEAAUkBAAtnZXRTbG90TWFzawEAAygpSQEALWNvZGVjaGlja2VuL21pY3JvYmxvY2svQ29tbW9uTWljcm9ibG9jayRjbGFzcwcAEgEALChMY29kZWNoaWNrZW4vbWljcm9ibG9jay9Db21tb25NaWNyb2Jsb2NrOylJDAAQABQKABMAFQEABHRoaXMBADBMY29kZWNoaWNrZW4vbXVsdGlwYXJ0L2NvbXBhdC9SZWZlcmVuY2VQb3N0RmFjZTsBABhnZXRQYXJ0aWFsT2NjbHVzaW9uQm94ZXMBABIoKUxqYXZhL3V0aWwvTGlzdDsBADsoTGNvZGVjaGlja2VuL21pY3JvYmxvY2svQ29tbW9uTWljcm9ibG9jazspTGphdmEvdXRpbC9MaXN0OwwAGQAbCgATABwBAAtpdGVtQ2xhc3NJRAwAHgAUCgATAB8BADtjb2RlY2hpY2tlbiRtaWNyb2Jsb2NrJFRNaWNyb09jY2x1c2lvbiQkc3VwZXIkb2NjbHVzaW9uVGVzdAEAJShMY29kZWNoaWNrZW4vbXVsdGlwYXJ0L1RNdWx0aVBhcnQ7KVoBACBjb2RlY2hpY2tlbi9tdWx0aXBhcnQvVE11bHRpUGFydAcAIwEADW9jY2x1c2lvblRlc3QMACUAIgoAJAAmAQAFbnBhcnQBACJMY29kZWNoaWNrZW4vbXVsdGlwYXJ0L1RNdWx0aVBhcnQ7AQAsY29kZWNoaWNrZW4vbWljcm9ibG9jay9UTWljcm9PY2NsdXNpb24kY2xhc3MHACoBAE0oTGNvZGVjaGlja2VuL21pY3JvYmxvY2svVE1pY3JvT2NjbHVzaW9uO0xjb2RlY2hpY2tlbi9tdWx0aXBhcnQvVE11bHRpUGFydDspWgwAJQAsCgArAC0BABdlZGdlQ29ybmVyT2NjbHVzaW9uVGVzdAEAUyhMY29kZWNoaWNrZW4vbWljcm9ibG9jay9UTWljcm9PY2NsdXNpb247TGNvZGVjaGlja2VuL21pY3JvYmxvY2svVE1pY3JvT2NjbHVzaW9uOylaAQB7KExjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL1RNaWNyb09jY2x1c2lvbjtMY29kZWNoaWNrZW4vbWljcm9ibG9jay9UTWljcm9PY2NsdXNpb247TGNvZGVjaGlja2VuL21pY3JvYmxvY2svVE1pY3JvT2NjbHVzaW9uOylaDAAvADEKACsAMgEABGVkZ2UBAChMY29kZWNoaWNrZW4vbWljcm9ibG9jay9UTWljcm9PY2NsdXNpb247AQAGY29ybmVyDAALAAwJAAIANwEACmV2ZW50c18kZXEBABMoTGphdmEvdXRpbC9MaXN0OylWAQADeCQxDAAOAA8JAAIAPAEACHNsb3RfJGVxAQAEKEkpVgEACm1pY3JvQ2xhc3MBACsoKUxjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL0NvbW1vbk1pY3JvQ2xhc3M7AQAHZ2V0VHlwZQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7DAALABoKAAIARAEACXR5cGU6ZmFjZQgARgEADmphdmEvdXRpbC9MaXN0BwBIAQADYWRkAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaDABKAEsLAEkATAEACG1jcl9mYWNlCABOAQAHZ2V0U2xvdAEACXNsb3Q6ZmFjZQgAUQwADgARCgACAFMBAAlnZXRCb3VuZHMBAB8oKUxjb2RlY2hpY2tlbi9saWIvdmVjL0N1Ym9pZDY7AQALYm91bmRzOmZhY2UIAFcBABtjb2RlY2hpY2tlbi9saWIvdmVjL0N1Ym9pZDYHAFkBAARmdWxsAQAdTGNvZGVjaGlja2VuL2xpYi92ZWMvQ3Vib2lkNjsMAFsAXAkAWgBdAQAWKClMamF2YS9sYW5nL0l0ZXJhYmxlOwwAGQAaCgACAGABACooKUxjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL01pY3JvYmxvY2tDbGFzczsMAEAAQQoAAgBjAQAGPGluaXQ+AQADKClWDABlAD8KAAQAZwEABiRpbml0JAEAKyhMY29kZWNoaWNrZW4vbWljcm9ibG9jay9UTWljcm9PY2NsdXNpb247KVYMAGkAagoAKwBrAQAsKExjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL0NvbW1vbk1pY3JvYmxvY2s7KVYMAGkAbQoAEwBuAQATamF2YS91dGlsL0FycmF5TGlzdAcAcAwAZQBmCgBxAHIBAAlTaWduYXR1cmUBAARDb2RlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAPTGluZU51bWJlclRhYmxlAQAxKClMamF2YS91dGlsL0xpc3Q8TGNvZGVjaGlja2VuL2xpYi92ZWMvQ3Vib2lkNjs+OwEAJigpTGphdmEvdXRpbC9MaXN0PExqYXZhL2xhbmcvU3RyaW5nOz47AQAnKExqYXZhL3V0aWwvTGlzdDxMamF2YS9sYW5nL1N0cmluZzs+OylWAQAKU291cmNlRmlsZQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBAAhTY2FsYVNpZwAhAAIABAABAAYAAgACAAsADAABAHQAAAACAA0AAgAOAA8AAAARAAEAEAARAAEAdQAAAC8AAQABAAAABSq4ABasAAAAAgB2AAAADAABAAAABQAXABgAAAB3AAAABgABAAAAMAABABkAGgACAHUAAAAvAAEAAQAAAAUquAAdsAAAAAIAdgAAAAwAAQAAAAUAFwAYAAAAdwAAAAYAAQAAADAAdAAAAAIAeAABAB4AEQABAHUAAAAvAAEAAQAAAAUquAAgrAAAAAIAdgAAAAwAAQAAAAUAFwAYAAAAdwAAAAYAAQAAADAQAQAhACIAAQB1AAAAOgACAAIAAAAGKiu3ACesAAAAAgB2AAAAFgACAAAABgAXABgAAAAAAAYAKAApAAEAdwAAAAYAAQAAADAAAQAlACIAAQB1AAAAOgACAAIAAAAGKiu4AC6sAAAAAgB2AAAAFgACAAAABgAXABgAAAAAAAYAKAApAAEAdwAAAAYAAQAAADAAAQAvADAAAQB1AAAARQADAAMAAAAHKissuAAzrAAAAAIAdgAAACAAAwAAAAcAFwAYAAAAAAAHADQANQABAAAABwA2ADUAAgB3AAAABgABAAAAMAABAAsAGgACAHUAAAAvAAEAAQAAAAUqtAA4sAAAAAIAdgAAAAwAAQAAAAUAFwAYAAAAdwAAAAYAAQAAADEAdAAAAAIAeQABADkAOgACAHUAAAA6AAIAAgAAAAYqK7UAOLEAAAACAHYAAAAWAAIAAAAGABcAGAAAAAAABgA7AAwAAQB3AAAABgABAAAAMQB0AAAAAgB6AAEADgARAAEAdQAAAC8AAQABAAAABSq0AD2sAAAAAgB2AAAADAABAAAABQAXABgAAAB3AAAABgABAAAAMgABAD4APwABAHUAAAA6AAIAAgAAAAYqG7UAPbEAAAACAHYAAAAWAAIAAAAGABcAGAAAAAAABgA7AA8AAQB3AAAABgABAAAAMgABAEAAQQABAHUAAAAsAAEAAQAAAAIBsAAAAAIAdgAAAAwAAQAAAAIAFwAYAAAAdwAAAAYAAQAAADMAAQBCAEMAAQB1AAAAOQACAAEAAAAPKrYARRJHuQBNAgBXEk+wAAAAAgB2AAAADAABAAAADwAXABgAAAB3AAAABgABAAAANAABAFAAEQABAHUAAAA7AAIAAQAAABEqtgBFElK5AE0CAFcqtgBUrAAAAAIAdgAAAAwAAQAAABEAFwAYAAAAdwAAAAYAAQAAADUAAQBVAFYAAQB1AAAAOgACAAEAAAAQKrYARRJYuQBNAgBXsgBesAAAAAIAdgAAAAwAAQAAABAAFwAYAAAAdwAAAAYAAQAAADYQQQAZAF8AAQB1AAAALwABAAEAAAAFKrYAYbAAAAACAHYAAAAMAAEAAAAFABcAGAAAAHcAAAAGAAEAAAAwEEEAQABiAAEAdQAAAC8AAQABAAAABSq2AGSwAAAAAgB2AAAADAABAAAABQAXABgAAAB3AAAABgABAAAAMAABAGUAZgABAHUAAABQAAMAAQAAAB4qA7cAaCq4AGwquABvKrsAcVm3AHO1ADgqA7UAPbEAAAACAHYAAAAMAAEAAAAeABcAGAAAAHcAAAAOAAMAAAAwAA0AMQAYADIAAwB7AAAAAgAHAHwAAAALAAEACAABAAlzAAoAfQAAAAMFAAA= diff --git a/src/test/resources/compat/ReferencePostMicroblockBase.class.b64 b/src/test/resources/compat/ReferencePostMicroblockBase.class.b64 new file mode 100644 index 0000000..f6d6cee --- /dev/null +++ b/src/test/resources/compat/ReferencePostMicroblockBase.class.b64 @@ -0,0 +1 @@ +yv66vgAAADQAXwEAOGNvZGVjaGlja2VuL211bHRpcGFydC9jb21wYXQvUmVmZXJlbmNlUG9zdE1pY3JvYmxvY2tCYXNlBwABAQAhY29kZWNoaWNrZW4vbWljcm9ibG9jay9NaWNyb2Jsb2NrBwADAQAiUmVmZXJlbmNlU2NhbGFQb3N0TWljcm9ibG9jay5zY2FsYQEAHkxzY2FsYS9yZWZsZWN0L1NjYWxhU2lnbmF0dXJlOwEABWJ5dGVzAQL8BgF9M1EhAQICAiURMURVM2dLSixnblkzUV9OJFgqWzJzXwpkd25ZNkNDTiwnQkECBQMZGXcuHDliaSoRUUFCAQpbVmRHLxs5YmVSVBFhQgEMRz4kV20ZNWpHLixnbgEBFAUBUQFDQQYPGwVhIUJBBwcDKWkXbhk6cEUyfDdtWwUDHzERISJUNWRlPhRHblwybBEVCQIBIgETAxlhFE5cNXV9UQkxAwUCFQE1CSEBQwQXAQEHSRFBDAINFTRYTRw7dCsFQQJjQQ0fQTUJIUQDAhw5BSFRD141bRUFaRIBAjZibQZMIWEIDgMJMUs3Dx4JA0MdciFBSRMOAw1SEQFKAQZnDgxHLlkFA00NCmEBFTpmSRY0F0IBFSoFGRlGT101b08qEWFlCQUIVwEBDREiAS0DKSlnLxo4dWd+I1MtHQsDW0ECIkEJGAoFPRojAUIrb1NSRHEhDRYCAgMHASRBAnlJRUJhYQ0BIUITQRJhQjN3SzokOA8JBQhrAQENESIBNwMVYRdNWTNtKwU5BEMBHTwbBUkkQgEeHQMRYRdNXDQKBSFKBGJCHwEBBCUJQVABClkGFFcNXDAlS0YkIiFMIAkPRWIUESFhAW8hMREJAVEhCl0KYQFcMWNLMgQDYkIiAQEEJQkBUgEMZ1YEWE0dKmZnVmRHL0YBRiEJEWMpAwJIRwk5IWlcOG1LBnQHYkIlAQEEJQlBUwEQZ1YEWE0dKmZnVmRHbxgTZmNSEVFmEwUIYyELCRExAUYRGWkFASlRBQsGYTEvHjlmZUorNy8eN3VBITlxCgFhAQoDIRUBRDp2YRYUaClZNW1rSiwHYkIpAQEEJQlBVQERZ1YEWE0dJGJTMiwoL1owJUtGJCIhTCoJD0UCFhEhYQELIjFRCwFRIQoVC1FiXTtxS0o0FS0bN3ZlFgQDIkIsAQkDQhYhRDhkRzIsOC9bOG8pFhxIDwYCRjMiKSFMFmEBNwYhAS8ZOnUhCWFWLEQBBRMJcUZBAQZVG1ZkRy8bKWJlUgQBAAZldmVudHMBABBMamF2YS91dGlsL0xpc3Q7AQAkTGphdmEvdXRpbC9MaXN0PExqYXZhL2xhbmcvU3RyaW5nOz47AQAFbGFiZWwBABJMamF2YS9sYW5nL1N0cmluZzsBAAtzdXBlclJlc3VsdAEAAVoBAAxzdXBlckZhaWx1cmUBABIoKUxqYXZhL3V0aWwvTGlzdDsMAAkACgkAAgASAQAEdGhpcwEAOkxjb2RlY2hpY2tlbi9tdWx0aXBhcnQvY29tcGF0L1JlZmVyZW5jZVBvc3RNaWNyb2Jsb2NrQmFzZTsBAApldmVudHNfJGVxAQATKExqYXZhL3V0aWwvTGlzdDspVgEAA3gkMQEAFCgpTGphdmEvbGFuZy9TdHJpbmc7DAAMAA0JAAIAGgEACWxhYmVsXyRlcQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEAAygpWgwADgAPCQACAB8BAA9zdXBlclJlc3VsdF8kZXEBAAQoWilWDAAQAA8JAAIAIwEAEHN1cGVyRmFpbHVyZV8kZXEBAA1vY2NsdXNpb25UZXN0AQAlKExjb2RlY2hpY2tlbi9tdWx0aXBhcnQvVE11bHRpUGFydDspWgwACQARCgACACgBACZzY2FsYS9jb2xsZWN0aW9uL211dGFibGUvU3RyaW5nQnVpbGRlcgcAKgEABjxpbml0PgEAAygpVgwALAAtCgArAC4BAAZzdXBlcjoIADABAAZhcHBlbmQBADwoTGphdmEvbGFuZy9PYmplY3Q7KUxzY2FsYS9jb2xsZWN0aW9uL211dGFibGUvU3RyaW5nQnVpbGRlcjsMADIAMwoAKwA0DAAMABkKAAIANgEACHRvU3RyaW5nDAA4ABkKACsAOQEADmphdmEvdXRpbC9MaXN0BwA7AQADYWRkAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaDAA9AD4LADwAPwwAEAAeCgACAEEBAB9qYXZhL2xhbmcvSWxsZWdhbFN0YXRlRXhjZXB0aW9uBwBDAQANc3VwZXIgZmFpbHVyZQgARQwALAAdCgBEAEcMAA4AHgoAAgBJAQAEcGFydAEAIkxjb2RlY2hpY2tlbi9tdWx0aXBhcnQvVE11bHRpUGFydDsBAAQoSSlWDAAsAE0KAAQATgEAE2phdmEvdXRpbC9BcnJheUxpc3QHAFAKAFEALgEABHNlbGYIAFMBAAlTaWduYXR1cmUBAARDb2RlAQASTG9jYWxWYXJpYWJsZVRhYmxlAQAPTGluZU51bWJlclRhYmxlAQAmKClMamF2YS91dGlsL0xpc3Q8TGphdmEvbGFuZy9TdHJpbmc7PjsBACcoTGphdmEvdXRpbC9MaXN0PExqYXZhL2xhbmcvU3RyaW5nOz47KVYBAA1TdGFja01hcFRhYmxlAQAKU291cmNlRmlsZQEAGVJ1bnRpbWVWaXNpYmxlQW5ub3RhdGlvbnMBAAhTY2FsYVNpZwQhAAIABAAAAAQAAgAJAAoAAQBVAAAAAgALAAIADAANAAAAAgAOAA8AAAACABAADwAAAAoAAQAJABEAAgBWAAAALwABAAEAAAAFKrQAE7AAAAACAFcAAAAMAAEAAAAFABQAFQAAAFgAAAAGAAEAAAAJAFUAAAACAFkAAQAWABcAAgBWAAAAOgACAAIAAAAGKiu1ABOxAAAAAgBXAAAAFgACAAAABgAUABUAAAAAAAYAGAAKAAEAWAAAAAYAAQAAAAkAVQAAAAIAWgABAAwAGQABAFYAAAAvAAEAAQAAAAUqtAAbsAAAAAIAVwAAAAwAAQAAAAUAFAAVAAAAWAAAAAYAAQAAAAoAAQAcAB0AAQBWAAAAOgACAAIAAAAGKiu1ABuxAAAAAgBXAAAAFgACAAAABgAUABUAAAAAAAYAGAANAAEAWAAAAAYAAQAAAAoAAQAOAB4AAQBWAAAALwABAAEAAAAFKrQAIKwAAAACAFcAAAAMAAEAAAAFABQAFQAAAFgAAAAGAAEAAAALAAEAIQAiAAEAVgAAADoAAgACAAAABiobtQAgsQAAAAIAVwAAABYAAgAAAAYAFAAVAAAAAAAGABgADwABAFgAAAAGAAEAAAALAAEAEAAeAAEAVgAAAC8AAQABAAAABSq0ACSsAAAAAgBXAAAADAABAAAABQAUABUAAABYAAAABgABAAAADAABACUAIgABAFYAAAA6AAIAAgAAAAYqG7UAJLEAAAACAFcAAAAWAAIAAAAGABQAFQAAAAAABgAYAA8AAQBYAAAABgABAAAADAABACYAJwABAFYAAAB7AAMAAgAAADYqtgApuwArWbcALxIxtgA1KrYAN7YANbYAOrkAQAIAVyq2AEKZAA27AERZEka3AEi/KrYASqwAAAADAFcAAAAWAAIAAAA2ABQAFQAAAAAANgBLAEwAAQBYAAAADgADAAAADwAgABAAMQARAFsAAAADAAExAAEALAAtAAEAVgAAAFwAAwABAAAAIioQH7cATyq7AFFZtwBStQATKhJUtQAbKgS1ACAqA7UAJLEAAAACAFcAAAAMAAEAAAAiABQAFQAAAFgAAAAWAAUAAAAIAAYACQARAAoAFwALABwADAADAFwAAAACAAUAXQAAAAsAAQAGAAEAB3MACABeAAAAAwUAAA== diff --git a/src/test/resources/compat/ReferenceScalaPostMicroblock.class.b64 b/src/test/resources/compat/ReferenceScalaPostMicroblock.class.b64 new file mode 100644 index 0000000..b327225 --- /dev/null +++ b/src/test/resources/compat/ReferenceScalaPostMicroblock.class.b64 @@ -0,0 +1 @@ +yv66vgAAADQAlgEAOWNvZGVjaGlja2VuL211bHRpcGFydC9jb21wYXQvUmVmZXJlbmNlU2NhbGFQb3N0TWljcm9ibG9jawcAAQEAOGNvZGVjaGlja2VuL211bHRpcGFydC9jb21wYXQvUmVmZXJlbmNlUG9zdE1pY3JvYmxvY2tCYXNlBwADAQAlY29kZWNoaWNrZW4vbWljcm9ibG9jay9Qb3N0TWljcm9ibG9jawcABQEAIlJlZmVyZW5jZVNjYWxhUG9zdE1pY3JvYmxvY2suc2NhbGEBAB5Mc2NhbGEvcmVmbGVjdC9TY2FsYVNpZ25hdHVyZTsBAAVieXRlcwEDNwYBfTNBIQECARMJYSIrGjRmZRZ0Ny1aKmRDMgwHa1w6dRsmHCdvHDJtXw5cJ0JBAgUDGRl3Lhw5YmkqEVFBQgEKW1ZkRy8bOWJlUlQRYUIBDEc+JFdtGTVqRy4sZ24BARQHAVFhAgUCDBk1CSEhAwIOBQlZIisaNGZlFnQ3LRopcGdSbBVuGTpwRTJ8N20bImJnFgQiYQQKDgNBUSEhBQQCFTVMN01dOGNZPhw3LgMCFCEJcQFrXDp1GyYcJ28cMm1fDlwHIkILAQkDMRJBAh9qXSYkaAhGARghCVkBAUMEGgEBB0kRAQ4CG00sRy5aMnVLEhwGLhk5ZisFWQJDAQ8gGwVpIiIBEAILTRwXDVwxCgUBaiJhQSVvaSI5IQUBYQEKAxkTIUU6ZlkWHEcvGjNUUQYEWG0YE2ZjUhFBZQoJAzkVSiFBSg8DCVVzFw4eBQhRBQoJETEBHAMNQUglDQUHVQEBCxVCDgIdTSxHLloydUsSHAYuGTlmQSE5QQYBYQEKA2kTQUQ6ZlkWHEcvGjNDX1Z0R21dCwJdQRFxBk4HAmEpERFHTQEEbRYcJ0JBGgcDDWEXTlkFA2tBEnFhUTtjXyYkZwdDBDgBAQdJEQEdAiVNLEcuWjJ1SxIUdS4eOGVnfiNTLR0LA0llQnEBCxwCAgMHYQYDBDwBAQZLQUwBEGcWZFdtGTtmSQp7V08cM3RBITlRCAFhAQoDcRQBBTpmYTIMNy1aN2ZdUhR1Dl8zdCsFeQRjASFGXTUJEUkDAkMHBiFRD141bRUFIRUBAjZibQZMIUFSIQMJMUs3Dx4FCBECAQ0RIgFKA1ERWA0dN2JHFmxXTRw7Q19iLDdvGBNmY1IRQUUTBQhRHQsJETEBQBEZYQUBKVEFfwUJIi8aOW1DDixXLho4dQU+RFhtHREJCzkDQRFJKAIRHSxHbxU1YmEWJBJhBwUGIwIhCUVVAQpPFiQoaVw7b0lOJBJBDAUGKQIhCSVWAQhPFiRIKx85ZikFMQZDQSxbGwVBJkJBLUQDEWEXTVw0CgVtQyZBQip1ZSZ0dw1DA14BEQVjLEEJaEtSfDVtGTd2ZyZ8Z05ROHlLTiQSYRABAA1zZWxlY3RlZFNoYXBlAQABSQEADnNlbGVjdGVkQm91bmRzAQAdTGNvZGVjaGlja2VuL2xpYi92ZWMvQ3Vib2lkNjsBABByZXBsYWNlbWVudEJveGVzAQAQTGphdmEvdXRpbC9MaXN0OwEAL0xqYXZhL3V0aWwvTGlzdDxMY29kZWNoaWNrZW4vbGliL3ZlYy9DdWJvaWQ2Oz47AQA6Y29kZWNoaWNrZW4kbWljcm9ibG9jayRQb3N0TWljcm9ibG9jayQkc3VwZXIkb2NjbHVzaW9uVGVzdAEAJShMY29kZWNoaWNrZW4vbXVsdGlwYXJ0L1RNdWx0aVBhcnQ7KVoBAA1vY2NsdXNpb25UZXN0DAAUABMKAAQAFQEABHRoaXMBADtMY29kZWNoaWNrZW4vbXVsdGlwYXJ0L2NvbXBhdC9SZWZlcmVuY2VTY2FsYVBvc3RNaWNyb2Jsb2NrOwEABW5wYXJ0AQAiTGNvZGVjaGlja2VuL211bHRpcGFydC9UTXVsdGlQYXJ0OwEACm1pY3JvQ2xhc3MBACooKUxjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL1Bvc3RNaWNyb0NsYXNzJDsBACtjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL1Bvc3RNaWNyb2Jsb2NrJGNsYXNzBwAdAQBRKExjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL1Bvc3RNaWNyb2Jsb2NrOylMY29kZWNoaWNrZW4vbWljcm9ibG9jay9Qb3N0TWljcm9DbGFzcyQ7DAAbAB8KAB4AIAEAGGdldFBhcnRpYWxPY2NsdXNpb25Cb3hlcwEAEigpTGphdmEvdXRpbC9MaXN0OwEAOShMY29kZWNoaWNrZW4vbWljcm9ibG9jay9Qb3N0TWljcm9ibG9jazspTGphdmEvdXRpbC9MaXN0OwwAIgAkCgAeACUBAAtpdGVtQ2xhc3NJRAEAAygpSQEAKihMY29kZWNoaWNrZW4vbWljcm9ibG9jay9Qb3N0TWljcm9ibG9jazspSQwAJwApCgAeACoBAEwoTGNvZGVjaGlja2VuL21pY3JvYmxvY2svUG9zdE1pY3JvYmxvY2s7TGNvZGVjaGlja2VuL211bHRpcGFydC9UTXVsdGlQYXJ0OylaDAAUACwKAB4ALQEAE2dldFJlc2lzdGFuY2VGYWN0b3IBAAMoKUYBACooTGNvZGVjaGlja2VuL21pY3JvYmxvY2svUG9zdE1pY3JvYmxvY2s7KUYMAC8AMQoAHgAyAQASY2FuUGxhY2VUb3JjaE9uVG9wAQADKClaAQAqKExjb2RlY2hpY2tlbi9taWNyb2Jsb2NrL1Bvc3RNaWNyb2Jsb2NrOylaDAA0ADYKAB4ANwwACwAMCQACADkBABFzZWxlY3RlZFNoYXBlXyRlcQEABChJKVYBAAN4JDEBAB8oKUxjb2RlY2hpY2tlbi9saWIvdmVjL0N1Ym9pZDY7DAANAA4JAAIAPwEAEnNlbGVjdGVkQm91bmRzXyRlcQEAIChMY29kZWNoaWNrZW4vbGliL3ZlYy9DdWJvaWQ2OylWDAAPABAJAAIAQwEAFHJlcGxhY2VtZW50Qm94ZXNfJGVxAQATKExqYXZhL3V0aWwvTGlzdDspVgEACGdldFNoYXBlAQAGZXZlbnRzDABIACMKAAIASQEAJnNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9TdHJpbmdCdWlsZGVyBwBLAQAGPGluaXQ+AQADKClWDABNAE4KAEwATwEABnNoYXBlOggAUQEABmFwcGVuZAEAPChMamF2YS9sYW5nL09iamVjdDspTHNjYWxhL2NvbGxlY3Rpb24vbXV0YWJsZS9TdHJpbmdCdWlsZGVyOwwAUwBUCgBMAFUBAAVsYWJlbAEAFCgpTGphdmEvbGFuZy9TdHJpbmc7DABXAFgKAAIAWQEACHRvU3RyaW5nDABbAFgKAEwAXAEADmphdmEvdXRpbC9MaXN0BwBeAQADYWRkAQAVKExqYXZhL2xhbmcvT2JqZWN0OylaDABgAGELAF8AYgwACwAoCgACAGQBAAlnZXRCb3VuZHMBAAdib3VuZHM6CABnDAANAD4KAAIAaQEAB2dldFR5cGUBAAV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