Extract out a base RoundtripTest and IdenticalRoundtripTest class
diff --git a/baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java b/baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java
new file mode 100644
index 0000000..e636ee1
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/IdenticalRoundtripTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+
+/**
+ * A base test class for performing a roundtrip assembly/disassembly where the input and output
+ * should be identical.
+ *
+ * By default, the input/output file should be a resource at [testDir]/[testName].smali
+ */
+public abstract class IdenticalRoundtripTest extends RoundtripTest {
+
+    public IdenticalRoundtripTest(@Nonnull String testDir) {
+        super(testDir);
+    }
+
+    public IdenticalRoundtripTest() {
+    }
+
+    @Nonnull @Override protected String getInputFilename(@Nonnull String testName) {
+        return String.format("%s%s%s.smali", testDir, File.separatorChar, testName);
+    }
+
+    @Nonnull @Override protected String getOutputFilename(@Nonnull String testName) {
+        return getInputFilename(testName);
+    }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java b/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java
index c4c6cae..a1a24c09 100644
--- a/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/LambdaTest.java
@@ -31,50 +31,22 @@
 
 package org.jf.baksmali;
 
-import com.google.common.io.ByteStreams;
-
 import org.antlr.runtime.RecognitionException;
-
-import org.junit.Assert;
 import org.junit.Test;
 
-import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 
-public class LambdaTest {
+public class LambdaTest extends IdenticalRoundtripTest {
+
+    private baksmaliOptions createOptions() {
+        baksmaliOptions options = new baksmaliOptions();
+        options.apiLevel = 23;  // since we need at least level 23 for lambda opcodes
+        options.experimental = true; // since these opcodes aren't implemented in runtime yet);
+        return options;
+    }
 
     @Test
     public void testHelloWorldLambda() throws IOException, RecognitionException {
-        runTest("HelloWorldLambda");
-    }
-
-    private void runTest(String fileName) throws IOException, RecognitionException {
-        // Load file from resources as a stream
-        String smaliFile = String.format("LambdaTest%s%s.smali", File.separatorChar, fileName);
-
-        InputStream smaliStream = LambdaTest.class.getClassLoader().
-                getResourceAsStream(smaliFile);
-        if (smaliStream == null) {
-            Assert.fail("Could not load " + smaliFile);
-        }
-
-        String source = readFully(smaliStream);
-
-        // Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
-        baksmaliOptions options = new baksmaliOptions();
-        options.apiLevel = 23;  // since we need at least level 23 for lambda opcodes
-        options.experimental = true; // since these opcodes aren't implemented in runtime yet
-        BaksmaliTestUtils.assertSmaliCompiledEquals(source, source, options, true);
-    }
-
-
-    public static String readFully(InputStream inputStream) throws IOException {
-        return readFully(inputStream, "UTF-8");
-    }
-
-    public static String readFully(InputStream inputStream, String encoding)
-            throws IOException {
-        return new String(ByteStreams.toByteArray(inputStream), encoding);
+        runTest("HelloWorldLambda", createOptions());
     }
 }
diff --git a/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java b/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java
new file mode 100644
index 0000000..7c1ef5d
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/RoundtripTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import com.google.common.io.ByteStreams;
+import org.antlr.runtime.RecognitionException;
+import org.junit.Assert;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A base test class for performing a roundtrip assembly/disassembly
+ *
+ * The test accepts a smali file as input, performs a smali -> dex -> smali roundtrip, and
+ * verifies that the result equals a known-good output smali file.
+ *
+ * By default, the input and output files should be resources at [testDir]/[testName]Input.smali
+ * and [testDir]/[testName]Output.smali respectively
+ */
+public abstract class RoundtripTest {
+    protected final String testDir;
+
+    protected RoundtripTest(@Nonnull String testDir) {
+        this.testDir = testDir;
+    }
+
+    protected RoundtripTest() {
+        this.testDir = this.getClass().getSimpleName();
+    }
+
+    @Nonnull
+    protected String getInputFilename(@Nonnull String testName) {
+        return String.format("%s%s%sInput.smali", testDir, File.separatorChar, testName);
+    }
+
+    @Nonnull
+    protected String getOutputFilename(@Nonnull String testName) {
+        return String.format("%s%s%sOutput.smali", testDir, File.separatorChar, testName);
+    }
+
+    protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options)
+            throws IOException, RecognitionException {
+        // Load file from resources as a stream
+        String inputFilename = getInputFilename(testName);
+        String input = readResourceFully(getInputFilename(testName));
+        String output;
+        if (getOutputFilename(testName).equals(inputFilename)) {
+            output = input;
+        } else {
+            output = readResourceFully(getOutputFilename(testName));
+        }
+
+        // Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
+        BaksmaliTestUtils.assertSmaliCompiledEquals(input, output, options, true);
+    }
+
+    @Nonnull
+    public static String readResourceFully(@Nonnull String fileName) throws IOException {
+        return readResourceFully(fileName, "UTF-8");
+    }
+
+    @Nonnull
+    public static String readResourceFully(@Nonnull String fileName, @Nonnull String encoding)
+            throws IOException {
+        InputStream smaliStream = RoundtripTest.class.getClassLoader().
+                getResourceAsStream(fileName);
+        if (smaliStream == null) {
+            Assert.fail("Could not load " + fileName);
+        }
+
+        return new String(ByteStreams.toByteArray(smaliStream), encoding);
+    }
+}