Refactor zipalign to allow unit tests

Test: Self-tested
Bug: NA
Change-Id: If5e42735853e1508fbeb9fb420a9a9f129c2218f
diff --git a/tools/zipalign/Android.bp b/tools/zipalign/Android.bp
index 8e6196d..b39c278 100644
--- a/tools/zipalign/Android.bp
+++ b/tools/zipalign/Android.bp
@@ -4,8 +4,8 @@
 // Zip alignment tool
 //
 
-cc_binary_host {
-    name: "zipalign",
+cc_library_host_static {
+    name: "libzipalign",
 
     srcs: [
         "ZipAlign.cpp",
@@ -13,11 +13,15 @@
         "ZipFile.cpp",
     ],
 
+    export_include_dirs: [
+        "include",
+    ],
+
     cflags: ["-Wall", "-Werror"],
 
     // NOTE: Do not add any shared_libs dependencies because they will break the
     // static_sdk_tools target.
-    static_libs: [
+    whole_static_libs: [
         "libutils",
         "libcutils",
         "liblog",
@@ -34,3 +38,31 @@
         },
     },
 }
+
+cc_binary_host {
+    name: "zipalign",
+
+    srcs: [
+        "ZipAlignMain.cpp",
+    ],
+
+    cflags: ["-Wall", "-Werror"],
+
+    static_libs: [
+        "libzipalign",
+    ]
+}
+
+cc_test_host {
+    name: "zipalign_tests",
+    srcs: [
+        "tests/src/*_test.cpp",
+    ],
+    static_libs: [
+        "libzipalign",
+        "libgmock",
+    ],
+    data: [
+         "tests/data/unaligned.zip",
+    ],
+}
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index eea1749..1851ac5 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -14,35 +14,13 @@
  * limitations under the License.
  */
 
-/*
- * Zip alignment tool
- */
 #include "ZipFile.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-using namespace android;
-
-/*
- * Show program usage.
- */
-void usage(void)
-{
-    fprintf(stderr, "Zip alignment utility\n");
-    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
-    fprintf(stderr,
-        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
-        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
-    fprintf(stderr,
-        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
-    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
-    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
-    fprintf(stderr, "  -p: memory page alignment for stored shared object files\n");
-    fprintf(stderr, "  -v: verbose output\n");
-    fprintf(stderr, "  -z: recompress using Zopfli\n");
-}
+namespace android {
 
 static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,
     ZipEntry* pEntry) {
@@ -126,7 +104,7 @@
  * Process a file.  We open the input and output files, failing if the
  * output file exists and "force" wasn't specified.
  */
-static int process(const char* inFileName, const char* outFileName,
+int process(const char* inFileName, const char* outFileName,
     int alignment, bool force, bool zopfli, bool pageAlignSharedLibs)
 {
     ZipFile zin, zout;
@@ -169,7 +147,7 @@
 /*
  * Verify the alignment of a zip archive.
  */
-static int verify(const char* fileName, int alignment, bool verbose,
+int verify(const char* fileName, int alignment, bool verbose,
     bool pageAlignSharedLibs)
 {
     ZipFile zipFile;
@@ -218,92 +196,4 @@
     return foundBad ? 1 : 0;
 }
 
-/*
- * Parse args.
- */
-int main(int argc, char* const argv[])
-{
-    bool wantUsage = false;
-    bool check = false;
-    bool force = false;
-    bool verbose = false;
-    bool zopfli = false;
-    bool pageAlignSharedLibs = false;
-    int result = 1;
-    int alignment;
-    char* endp;
-
-    if (argc < 4) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    argc--;
-    argv++;
-
-    while (argc && argv[0][0] == '-') {
-        const char* cp = argv[0] +1;
-
-        while (*cp != '\0') {
-            switch (*cp) {
-            case 'c':
-                check = true;
-                break;
-            case 'f':
-                force = true;
-                break;
-            case 'v':
-                verbose = true;
-                break;
-            case 'z':
-                zopfli = true;
-                break;
-            case 'p':
-                pageAlignSharedLibs = true;
-                break;
-            default:
-                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
-                wantUsage = true;
-                goto bail;
-            }
-
-            cp++;
-        }
-
-        argc--;
-        argv++;
-    }
-
-    if (!((check && argc == 2) || (!check && argc == 3))) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    alignment = strtol(argv[0], &endp, 10);
-    if (*endp != '\0' || alignment <= 0) {
-        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
-        wantUsage = true;
-        goto bail;
-    }
-
-    if (check) {
-        /* check existing archive for correct alignment */
-        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
-    } else {
-        /* create the new archive */
-        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
-
-        /* trust, but verify */
-        if (result == 0) {
-            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
-        }
-    }
-
-bail:
-    if (wantUsage) {
-        usage();
-        result = 2;
-    }
-
-    return result;
-}
+} // namespace android
diff --git a/tools/zipalign/ZipAlignMain.cpp b/tools/zipalign/ZipAlignMain.cpp
new file mode 100644
index 0000000..49be916
--- /dev/null
+++ b/tools/zipalign/ZipAlignMain.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Zip alignment tool
+ */
+
+#include "ZipAlign.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace android;
+
+/*
+ * Show program usage.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Zip alignment utility\n");
+    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
+    fprintf(stderr,
+        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
+        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
+    fprintf(stderr,
+        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
+    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
+    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
+    fprintf(stderr, "  -p: memory page alignment for stored shared object files\n");
+    fprintf(stderr, "  -v: verbose output\n");
+    fprintf(stderr, "  -z: recompress using Zopfli\n");
+}
+
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+    bool wantUsage = false;
+    bool check = false;
+    bool force = false;
+    bool verbose = false;
+    bool zopfli = false;
+    bool pageAlignSharedLibs = false;
+    int result = 1;
+    int alignment;
+    char* endp;
+
+    if (argc < 4) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    argc--;
+    argv++;
+
+    while (argc && argv[0][0] == '-') {
+        const char* cp = argv[0] +1;
+
+        while (*cp != '\0') {
+            switch (*cp) {
+            case 'c':
+                check = true;
+                break;
+            case 'f':
+                force = true;
+                break;
+            case 'v':
+                verbose = true;
+                break;
+            case 'z':
+                zopfli = true;
+                break;
+            case 'p':
+                pageAlignSharedLibs = true;
+                break;
+            default:
+                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
+                wantUsage = true;
+                goto bail;
+            }
+
+            cp++;
+        }
+
+        argc--;
+        argv++;
+    }
+
+    if (!((check && argc == 2) || (!check && argc == 3))) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    alignment = strtol(argv[0], &endp, 10);
+    if (*endp != '\0' || alignment <= 0) {
+        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
+        wantUsage = true;
+        goto bail;
+    }
+
+    if (check) {
+        /* check existing archive for correct alignment */
+        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
+    } else {
+        /* create the new archive */
+        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
+
+        /* trust, but verify */
+        if (result == 0) {
+            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
+        }
+    }
+
+bail:
+    if (wantUsage) {
+        usage();
+        result = 2;
+    }
+
+    return result;
+}
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
index 810d74a..5233f0a 100644
--- a/tools/zipalign/ZipEntry.cpp
+++ b/tools/zipalign/ZipEntry.cpp
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <time.h>
 
-using namespace android;
+namespace android {
 
 /*
  * Initialize a new ZipEntry structure from a FILE* positioned at a
@@ -696,3 +696,5 @@
         ALOGD("  comment: '%s'\n", mFileComment);
 }
 
+} // namespace android
+
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
index 88505b7..29d1bc6 100644
--- a/tools/zipalign/ZipFile.cpp
+++ b/tools/zipalign/ZipFile.cpp
@@ -35,7 +35,7 @@
 #include <assert.h>
 #include <inttypes.h>
 
-using namespace android;
+namespace android {
 
 /*
  * Some environments require the "b", some choke on it.
@@ -134,7 +134,7 @@
 /*
  * Return the Nth entry in the archive.
  */
-android::ZipEntry* ZipFile::getEntryByIndex(int idx) const
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
 {
     if (idx < 0 || idx >= (int) mEntries.size())
         return NULL;
@@ -145,7 +145,7 @@
 /*
  * Find an entry by name.
  */
-android::ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
 {
     /*
      * Do a stupid linear string-compare search.
@@ -1397,3 +1397,4 @@
         mCentralDirSize, mCentralDirOffset, mCommentLen);
 }
 
+} // namespace android
diff --git a/tools/zipalign/include/ZipAlign.h b/tools/zipalign/include/ZipAlign.h
new file mode 100644
index 0000000..ab36086
--- /dev/null
+++ b/tools/zipalign/include/ZipAlign.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ZIPALIGN_H
+#define ZIPALIGN_H
+
+namespace android {
+
+/*
+ * Generate a new, aligned, zip "output" from an "input" zip.
+ * - alignTo: Alignment (in bytes) for uncompressed entries.
+ * - force  : Overwrite output if it exists, fail otherwise.
+ * - zopfli : Recompress compressed entries with more efficient algorithm.
+ *            Copy compressed entries as-is, and unaligned, otherwise.
+ * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ *   alignTo, or all files to alignTo if false..
+ *
+ * Returns 0 on success.
+ */
+int process(const char* input, const char* output, int alignTo, bool force,
+    bool zopfli, bool pageAlignSharedLibs);
+
+/*
+ * Verify the alignment of a zip archive.
+ * - alignTo: Alignment (in bytes) for uncompressed entries.
+ * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ *   alignTo, or all files to alignTo if false..
+ *
+ * Returns 0 on success.
+ */
+int verify(const char* fileName, int alignTo, bool verbose,
+    bool pageAlignSharedLibs);
+
+} // namespace android
+
+#endif // ZIPALIGN_H
diff --git a/tools/zipalign/tests/data/unaligned.zip b/tools/zipalign/tests/data/unaligned.zip
new file mode 100644
index 0000000..d572b1a
--- /dev/null
+++ b/tools/zipalign/tests/data/unaligned.zip
Binary files differ
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
new file mode 100644
index 0000000..b8f2e15
--- /dev/null
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -0,0 +1,15 @@
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "ZipAlign.h"
+
+#include <stdio.h>
+
+using namespace android;
+
+TEST(Align, Unaligned) {
+  const char* src = "tests/data/unaligned.zip";
+  const char* dst = "tests/data/unaligned_out.zip";
+  int result = process(src, dst, 4, true, false, 4096);
+  ASSERT_EQ(0, result);
+}