package patdroid.regtest;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.common.io.Files;
import com.google.common.io.PatternFilenameFilter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import patdroid.core.ClassInfo;
import patdroid.core.MethodInfo;
import patdroid.dalvik.Instruction;
import patdroid.smali.SmaliClassDetailLoader;

import java.io.*;
import java.util.*;
import java.util.logging.Logger;
import java.util.zip.ZipFile;

import static org.junit.Assert.assertEquals;

/**
 * Regression test: reads APK and compare with dump files.
 *
 * <p>Specify -Dregtest.apkpath=<apk path> to run regression test.
 * <p>Also specify -Dregtest.updatedump=true to update dump files.
 */
@RunWith(Parameterized.class)
public class RegTest {
    private static final int API_LEVEL = 19;
    private static final Logger logger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME);

    private final File apkFile;
    private final File dumpFile;
    private final boolean updateDump;
    private BufferedReader dumpReader;
    private BufferedWriter dumpWriter;
    private int lineNumber;

    public RegTest(File apkFile, File dumpFile, boolean updateDump) {
        this.apkFile = apkFile;
        this.dumpFile = dumpFile;
        this.updateDump = updateDump;
    }

    @Before
    public void setUp() throws IOException {
        if (updateDump) {
            this.dumpWriter = new BufferedWriter(new FileWriter(dumpFile));
        } else {
            this.dumpReader = new BufferedReader(new FileReader(dumpFile));
        }
        this.lineNumber = 0;
    }

    @After
    public void tearDown() throws IOException {
        if (updateDump) {
            this.dumpWriter.close();
        }
        ClassInfo.globalScope.reset();
    }

    @Test
    public void run() throws IOException {
        if (updateDump) {
            logger.info("Updating dump for " + apkFile);
        } else {
            logger.info("Running regression test for " + apkFile);
        }

        SmaliClassDetailLoader.getFrameworkClassLoader(API_LEVEL).loadAll();
        new SmaliClassDetailLoader(new ZipFile(apkFile), true).loadAll();

        List<ClassInfo> sortedClasses = Ordering.usingToString().sortedCopy(ClassInfo.getAllClasses());
        for (ClassInfo c : sortedClasses) {
            if (!c.isFrameworkClass()) {
                handleEntry(c.toString());
                List<MethodInfo> sortedMethods = Ordering.usingToString().sortedCopy(c.getAllMethods());
                for (MethodInfo m : sortedMethods) {
                    handleEntry("\t" + m);
                    if (m.insns == null) {
                        handleEntry("\t\t(no instructions)");
                    } else {
                        for (Instruction i : m.insns) {
                            handleEntry("\t\t" + i);
                        }
                    }
                }
            }
        }
    }

    private void handleEntry(String entry) throws IOException {
        for (String line : entry.split(System.getProperty("line.separator"))) {
            ++lineNumber;
            if (updateDump) {
                this.dumpWriter.write(line);
                this.dumpWriter.newLine();
            } else {
                assertEquals("line " + lineNumber, this.dumpReader.readLine(), line);
            }
        }
    }

    @Parameterized.Parameters
    public static List<Object[]> params() {
        String apkPath = System.getProperty("regtest.apkpath", "");
        if (apkPath.isEmpty()) {
            logger.warning("regtest.apkpath not set, skipping regression test.");
            return ImmutableList.of();
        }
        String updateDumpProperty = System.getProperty("regtest.updatedump", "");
        boolean updateDump = updateDumpProperty.equalsIgnoreCase("true");
        File apkDir = new File(apkPath);
        File[] apkFiles = apkDir.listFiles(new PatternFilenameFilter("^.*.apk"));
        ImmutableList.Builder<Object[]> params = ImmutableList.builder();
        for (File apkFile : apkFiles) {
            String dumpFileName = Files.getNameWithoutExtension(apkFile.getName()) + ".txt";
            File dumpFile = new File(apkFile.getParentFile(), dumpFileName);
            params.add(new Object[]{apkFile, dumpFile, updateDump});
        }
        return params.build();
    }
}

@@ -636,16 +636,62 @@
 tEquals;
+%0Aimport static org.junit.Assert.assertNotNull;
 %0A%0A/**%0A *
@@ -2491,9 +2491,8 @@
 if (
-!
 c.is
@@ -2507,32 +2507,68 @@
 kClass()) %7B%0A    
+            continue;%0A            %7D%0A
             hand
@@ -2586,32 +2586,151 @@
 oString());%0A    
+        if (c.isMissing()) %7B%0A                handleEntry(%22%5Ct(missing class)%22);%0A                continue;%0A            %7D%0A
             List
@@ -2810,28 +2810,24 @@
 Methods());%0A
-    
             
@@ -2871,36 +2871,32 @@
                 
-    
 handleEntry(%22%5Ct%22
@@ -2902,20 +2902,16 @@
 %22 + m);%0A
-    
         
@@ -2953,36 +2953,32 @@
                 
-   
- 
 handleEntry(%22%5Ct%5C
@@ -3007,36 +3007,32 @@
                 
-    
 %7D else %7B%0A       
@@ -3040,28 +3040,24 @@
             
- 
-   
 for (Instruc
@@ -3092,36 +3092,32 @@
                 
-  
-  
 handleEntry(%22%5Ct%5C
@@ -3129,34 +3129,8 @@
 i);%0A
-                        %7D%0A
     
@@ -3272,76 +3272,157 @@
     
-for (String line : entry.split(System.getProperty(%22line.separator%22))
+BufferedReader entryReader = new BufferedReader(new StringReader(entry));%0A        String line;%0A        while ((line = entryReader.readLine()) != null
 ) %7B%0A
@@ -4284,24 +4284,81 @@
 %5E.*.apk%22));%0A
+        assertNotNull(%22failed to list files%22, apkFiles);%0A
         Immu

fix missing class and "\r" mismatch