diff options
Diffstat (limited to 'scripts')
-rw-r--r-- | scripts/AndroidManifest.tests.template | 21 | ||||
-rw-r--r-- | scripts/README_add-ons.txt | 2 | ||||
-rw-r--r-- | scripts/alias_rules.xml | 52 | ||||
-rw-r--r-- | scripts/android_rules.xml | 280 | ||||
-rw-r--r-- | scripts/build.alias.template | 49 | ||||
-rw-r--r-- | scripts/build.template | 269 | ||||
-rwxr-xr-x | scripts/combine_sdks.sh | 62 | ||||
-rw-r--r-- | scripts/default.properties.template | 13 | ||||
-rwxr-xr-x | scripts/divide_and_compress.py | 352 | ||||
-rw-r--r-- | scripts/divide_and_compress_constants.py | 60 | ||||
-rw-r--r-- | scripts/java_tests_file.template | 21 | ||||
-rw-r--r-- | scripts/plugin.prop | 2 | ||||
-rwxr-xr-x | scripts/test_divide_and_compress.py | 490 |
13 files changed, 1360 insertions, 313 deletions
diff --git a/scripts/AndroidManifest.tests.template b/scripts/AndroidManifest.tests.template new file mode 100644 index 0000000..1f7d827 --- /dev/null +++ b/scripts/AndroidManifest.tests.template @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="PACKAGE.tests" + android:versionCode="1" + android:versionName="1.0"> + <!-- We add an application tag here just so that we can indicate that + this package needs to link against the android.test library, + which is needed when building test cases. --> + <application> + <uses-library android:name="android.test.runner" /> + </application> + <!-- + This declares that this application uses the instrumentation test runner targeting + the package of PACKAGE. To run the tests use the command: + "adb shell am instrument -w PACKAGE.tests/android.test.InstrumentationTestRunner" + --> + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="PACKAGE" + android:label="Tests for ACTIVITY_NAME"/> +</manifest> diff --git a/scripts/README_add-ons.txt b/scripts/README_add-ons.txt new file mode 100644 index 0000000..b8eb1d6 --- /dev/null +++ b/scripts/README_add-ons.txt @@ -0,0 +1,2 @@ +Add-on folder. +Drop vendor supplied SDK add-on in this folder.
\ No newline at end of file diff --git a/scripts/alias_rules.xml b/scripts/alias_rules.xml new file mode 100644 index 0000000..0443193 --- /dev/null +++ b/scripts/alias_rules.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" ?> +<project name="alias_rules" default="package"> + + <!-- No user servicable parts below. --> + + <!-- Input directories --> + <property name="resource-dir" value="res" /> + + <!-- The final package file to generate --> + <property name="out-package" value="${ant.project.name}.apk" /> + + <!-- Tools --> + <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" > + <os family="windows"/> + </condition> + <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" > + <os family="windows"/> + </condition> + <property name="android-jar" value="${sdk-folder}/android.jar" /> + + <!-- Rules --> + + <!-- Packages the manifest and the resource files --> + <target name="package-res"> + <echo>Packaging resources...</echo> + <exec executable="${aapt}" failonerror="true"> + <arg value="package" /> + <arg value="-f" /> + <arg value="-M" /> + <arg value="AndroidManifest.xml" /> + <arg value="-S" /> + <arg value="${resource-dir}" /> + <arg value="-I" /> + <arg value="${android-jar}" /> + <arg value="-F" /> + <arg value="${out-package}" /> + </exec> + </target> + + <!-- Create the package file for this project from the sources. --> + <target name="package" depends="package-res" /> + + <!-- Create the package and install package on the default emulator --> + <target name="install" depends="package"> + <echo>Sending package to default emulator...</echo> + <exec executable="${adb}" failonerror="true"> + <arg value="install" /> + <arg value="${out-package}" /> + </exec> + </target> + +</project> diff --git a/scripts/android_rules.xml b/scripts/android_rules.xml new file mode 100644 index 0000000..bed5f24 --- /dev/null +++ b/scripts/android_rules.xml @@ -0,0 +1,280 @@ +<?xml version="1.0" ?> +<project name="android_rules" default="debug"> + + <!-- No user servicable parts below. --> + + <property name="outdir-main" value="../${outdir}" /> + + <property name="android-tools" value="${sdk-folder}/tools" /> + <property name="android-platform" value="${sdk-folder}/platforms/${target-folder}" /> + <property name="android-framework" value="${android-platform}/framework.aidl" /> + <property name="android-jar" value="${android-platform}/android.jar" /> + + <!-- Input directories --> + <property name="resource-dir" value="res" /> + <property name="asset-dir" value="assets" /> + <property name="srcdir" value="src" /> + <condition property="srcdir-ospath" + value="${basedir}\${srcdir}" + else="${basedir}/${srcdir}" > + <os family="windows"/> + </condition> + + <!-- folder for the 3rd party java libraries --> + <property name="external-libs" value="libs" /> + <condition property="external-libs-ospath" + value="${basedir}\${external-libs}" + else="${basedir}/${external-libs}" > + <os family="windows"/> + </condition> + + <!-- folder for the native libraries --> + <property name="native-libs" value="libs" /> + <condition property="native-libs-ospath" + value="${basedir}\${native-libs}" + else="${basedir}/${native-libs}" > + <os family="windows"/> + </condition> + + <!-- Output directories --> + <property name="outdir-classes" value="${outdir}/classes" /> + <condition property="outdir-classes-ospath" + value="${basedir}\${outdir-classes}" + else="${basedir}/${outdir-classes}" > + <os family="windows"/> + </condition> + <condition property="outdir-main-classes" + value="${outdir-main}/classes"> + <available file="${outdir-main}/classes" + type="dir"/> + </condition> + + <!-- Create R.java in the source directory --> + <property name="outdir-r" value="src" /> + + <!-- Intermediate files --> + <property name="dex-file" value="classes.dex" /> + <property name="intermediate-dex" value="${outdir}/${dex-file}" /> + <condition property="intermediate-dex-ospath" + value="${basedir}\${intermediate-dex}" + else="${basedir}/${intermediate-dex}" > + <os family="windows"/> + </condition> + + <!-- The final package file to generate --> + <property name="resources-package" value="${outdir}/${ant.project.name}.ap_" /> + <condition property="resources-package-ospath" + value="${basedir}\${resources-package}" + else="${basedir}/${resources-package}" > + <os family="windows"/> + </condition> + + <property name="out-debug-package" value="${outdir}/${ant.project.name}-debug.apk" /> + <condition property="out-debug-package-ospath" + value="${basedir}\${out-debug-package}" + else="${basedir}/${out-debug-package}" > + <os family="windows"/> + </condition> + + <property name="out-unsigned-package" value="${outdir}/${ant.project.name}-unsigned.apk" /> + <condition property="out-unsigned-package-ospath" + value="${basedir}\${out-unsigned-package}" + else="${basedir}/${out-unsigned-package}" > + <os family="windows"/> + </condition> + + <!-- Tools --> + <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" > + <os family="windows"/> + </condition> + <condition property="aidl" value="${android-tools}/aidl.exe" else="${android-tools}/aidl" > + <os family="windows"/> + </condition> + <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" > + <os family="windows"/> + </condition> + <condition property="dx" value="${android-tools}/dx.bat" else="${android-tools}/dx" > + <os family="windows"/> + </condition> + <condition property="apk-builder" value="${android-tools}/apkbuilder.bat" else="${android-tools}/apkbuilder" > + <os family="windows"/> + </condition> + + <!-- Rules --> + + <!-- Create the output directories if they don't exist yet. --> + <target name="dirs"> + <echo>Creating output directories if needed...</echo> + <mkdir dir="${outdir}" /> + <mkdir dir="${outdir-classes}" /> + </target> + + <!-- Generate the R.java file for this project's resources. --> + <target name="resource-src" depends="dirs"> + <echo>Generating R.java / Manifest.java from the resources...</echo> + <exec executable="${aapt}" failonerror="true"> + <arg value="package" /> + <arg value="-m" /> + <arg value="-J" /> + <arg value="${outdir-r}" /> + <arg value="-M" /> + <arg value="AndroidManifest.xml" /> + <arg value="-S" /> + <arg value="${resource-dir}" /> + <arg value="-I" /> + <arg value="${android-jar}" /> + </exec> + </target> + + <!-- Generate java classes from .aidl files. --> + <target name="aidl" depends="dirs"> + <echo>Compiling aidl files into Java classes...</echo> + <apply executable="${aidl}" failonerror="true"> + <arg value="-p${android-framework}" /> + <arg value="-I${srcdir}" /> + <fileset dir="${srcdir}"> + <include name="**/*.aidl"/> + </fileset> + </apply> + </target> + + <!-- Compile this project's .java files into .class files. --> + <target name="compile" depends="dirs, resource-src, aidl"> + <javac encoding="ascii" target="1.5" debug="true" extdirs="" + srcdir="${srcdir}" + destdir="${outdir-classes}" + bootclasspath="${android-jar}"> + <classpath> + <fileset dir="${external-libs}" includes="*.jar"/> + <pathelement path="${outdir-main-classes}"/> + </classpath> + </javac> + </target> + + <!-- Convert this project's .class files into .dex files. --> + <target name="dex" depends="compile"> + <echo>Converting compiled files and external libraries into ${outdir}/${dex-file}...</echo> + <apply executable="${dx}" failonerror="true" parallel="true"> + <arg value="--dex" /> + <arg value="--output=${intermediate-dex-ospath}" /> + <arg path="${outdir-classes-ospath}" /> + <fileset dir="${external-libs}" includes="*.jar"/> + </apply> + </target> + + <!-- Put the project's resources into the output package file. --> + <target name="package-res-and-assets"> + <echo>Packaging resources and assets...</echo> + <exec executable="${aapt}" failonerror="true"> + <arg value="package" /> + <arg value="-f" /> + <arg value="-M" /> + <arg value="AndroidManifest.xml" /> + <arg value="-S" /> + <arg value="${resource-dir}" /> + <arg value="-A" /> + <arg value="${asset-dir}" /> + <arg value="-I" /> + <arg value="${android-jar}" /> + <arg value="-F" /> + <arg value="${resources-package}" /> + </exec> + </target> + + <!-- Same as package-res-and-assets, but without "-A ${asset-dir}" --> + <target name="package-res-no-assets"> + <echo>Packaging resources...</echo> + <exec executable="${aapt}" failonerror="true"> + <arg value="package" /> + <arg value="-f" /> + <arg value="-M" /> + <arg value="AndroidManifest.xml" /> + <arg value="-S" /> + <arg value="${resource-dir}" /> + <!-- No assets directory --> + <arg value="-I" /> + <arg value="${android-jar}" /> + <arg value="-F" /> + <arg value="${resources-package}" /> + </exec> + </target> + + <!-- Invoke the proper target depending on whether or not + an assets directory is present. --> + <!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument + only when the assets dir exists. --> + <target name="package-res"> + <available file="${asset-dir}" type="dir" + property="res-target" value="and-assets" /> + <property name="res-target" value="no-assets" /> + <antcall target="package-res-${res-target}" /> + </target> + + <!-- Package the application and sign it with a debug key. + This is the default target when building. It is used for debug. --> + <target name="debug" depends="dex, package-res"> + <echo>Packaging ${out-debug-package}, and signing it with a debug key...</echo> + <exec executable="${apk-builder}" failonerror="true"> + <arg value="${out-debug-package-ospath}" /> + <arg value="-z" /> + <arg value="${resources-package-ospath}" /> + <arg value="-f" /> + <arg value="${intermediate-dex-ospath}" /> + <arg value="-rf" /> + <arg value="${srcdir-ospath}" /> + <arg value="-rj" /> + <arg value="${external-libs-ospath}" /> + <arg value="-nf" /> + <arg value="${native-libs-ospath}" /> + </exec> + </target> + + <!-- Package the application without signing it. + This allows for the application to be signed later with an official publishing key. --> + <target name="release" depends="dex, package-res"> + <echo>Packaging ${out-unsigned-package} for release...</echo> + <exec executable="${apk-builder}" failonerror="true"> + <arg value="${out-unsigned-package-ospath}" /> + <arg value="-u" /> + <arg value="-z" /> + <arg value="${resources-package-ospath}" /> + <arg value="-f" /> + <arg value="${intermediate-dex-ospath}" /> + <arg value="-rf" /> + <arg value="${srcdir-ospath}" /> + <arg value="-rj" /> + <arg value="${external-libs-ospath}" /> + <arg value="-nf" /> + <arg value="${native-libs-ospath}" /> + </exec> + <echo>It will need to be signed with jarsigner before being published.</echo> + </target> + + <!-- Install the package on the default emulator --> + <target name="install" depends="debug"> + <echo>Installing ${out-debug-package} onto default emulator...</echo> + <exec executable="${adb}" failonerror="true"> + <arg value="install" /> + <arg value="${out-debug-package}" /> + </exec> + </target> + + <target name="reinstall" depends="debug"> + <echo>Installing ${out-debug-package} onto default emulator...</echo> + <exec executable="${adb}" failonerror="true"> + <arg value="install" /> + <arg value="-r" /> + <arg value="${out-debug-package}" /> + </exec> + </target> + + <!-- Uinstall the package from the default emulator --> + <target name="uninstall"> + <echo>Uninstalling ${application-package} from the default emulator...</echo> + <exec executable="${adb}" failonerror="true"> + <arg value="uninstall" /> + <arg value="${application-package}" /> + </exec> + </target> + +</project> diff --git a/scripts/build.alias.template b/scripts/build.alias.template index 22252aa..b85887e 100644 --- a/scripts/build.alias.template +++ b/scripts/build.alias.template @@ -11,52 +11,9 @@ <property file="default.properties"/> <!-- ************************************************************************************* --> - <!-- No user servicable parts below. --> + <!-- Import the default Android build rules. + This requires ant 1.6.0 or above. --> - <!-- Input directories --> - <property name="resource-dir" value="res" /> - - <!-- The final package file to generate --> - <property name="out-package" value="${ant.project.name}.apk" /> - - <!-- Tools --> - <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" > - <os family="windows"/> - </condition> - <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" > - <os family="windows"/> - </condition> - <property name="android-jar" value="${sdk-folder}/android.jar" /> - - <!-- Rules --> - - <!-- Packages the manifest and the resource files --> - <target name="package-res"> - <echo>Packaging resources...</echo> - <exec executable="${aapt}" failonerror="true"> - <arg value="package" /> - <arg value="-f" /> - <arg value="-M" /> - <arg value="AndroidManifest.xml" /> - <arg value="-S" /> - <arg value="${resource-dir}" /> - <arg value="-I" /> - <arg value="${android-jar}" /> - <arg value="-F" /> - <arg value="${out-package}" /> - </exec> - </target> - - <!-- Create the package file for this project from the sources. --> - <target name="package" depends="package-res" /> - - <!-- Create the package and install package on the default emulator --> - <target name="install" depends="package"> - <echo>Sending package to default emulator...</echo> - <exec executable="${adb}" failonerror="true"> - <arg value="install" /> - <arg value="${out-package}" /> - </exec> - </target> + <import file="${sdk-folder}/tools/lib/alias_rules.xml" /> </project> diff --git a/scripts/build.template b/scripts/build.template index 0081c33..f04f1d8 100644 --- a/scripts/build.template +++ b/scripts/build.template @@ -22,272 +22,9 @@ <property name="outdir" value="bin" /> <!-- ************************************************************************************* --> - <!-- No user servicable parts below. --> + <!-- Import the default Android build rules. + This requires ant 1.6.0 or above. --> - <property name="android-tools" value="${sdk-folder}/tools" /> - <property name="android-framework" value="${android-tools}/lib/framework.aidl" /> - - <!-- Input directories --> - <property name="resource-dir" value="res" /> - <property name="asset-dir" value="assets" /> - <property name="srcdir" value="src" /> - <condition property="srcdir-ospath" - value="${basedir}\${srcdir}" - else="${basedir}/${srcdir}" > - <os family="windows"/> - </condition> - - <!-- folder for the 3rd party java libraries --> - <property name="external-libs" value="libs" /> - <condition property="external-libs-ospath" - value="${basedir}\${external-libs}" - else="${basedir}/${external-libs}" > - <os family="windows"/> - </condition> - - <!-- folder for the native libraries --> - <property name="native-libs" value="libs" /> - <condition property="native-libs-ospath" - value="${basedir}\${native-libs}" - else="${basedir}/${native-libs}" > - <os family="windows"/> - </condition> - - <!-- Output directories --> - <property name="outdir-classes" value="${outdir}/classes" /> - <condition property="outdir-classes-ospath" - value="${basedir}\${outdir-classes}" - else="${basedir}/${outdir-classes}" > - <os family="windows"/> - </condition> - - <!-- Create R.java in the source directory --> - <property name="outdir-r" value="src" /> - - <!-- Intermediate files --> - <property name="dex-file" value="classes.dex" /> - <property name="intermediate-dex" value="${outdir}/${dex-file}" /> - <condition property="intermediate-dex-ospath" - value="${basedir}\${intermediate-dex}" - else="${basedir}/${intermediate-dex}" > - <os family="windows"/> - </condition> - - <!-- The final package file to generate --> - <property name="resources-package" value="${outdir}/${ant.project.name}.ap_" /> - <condition property="resources-package-ospath" - value="${basedir}\${resources-package}" - else="${basedir}/${resources-package}" > - <os family="windows"/> - </condition> - - <property name="out-debug-package" value="${outdir}/${ant.project.name}-debug.apk" /> - <condition property="out-debug-package-ospath" - value="${basedir}\${out-debug-package}" - else="${basedir}/${out-debug-package}" > - <os family="windows"/> - </condition> - - <property name="out-unsigned-package" value="${outdir}/${ant.project.name}-unsigned.apk" /> - <condition property="out-unsigned-package-ospath" - value="${basedir}\${out-unsigned-package}" - else="${basedir}/${out-unsigned-package}" > - <os family="windows"/> - </condition> - - <!-- Tools --> - <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" > - <os family="windows"/> - </condition> - <condition property="aidl" value="${android-tools}/aidl.exe" else="${android-tools}/aidl" > - <os family="windows"/> - </condition> - <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" > - <os family="windows"/> - </condition> - <condition property="dx" value="${android-tools}/dx.bat" else="${android-tools}/dx" > - <os family="windows"/> - </condition> - <condition property="apk-builder" value="${android-tools}/apkbuilder.bat" else="${android-tools}/apkbuilder" > - <os family="windows"/> - </condition> - - <property name="android-jar" value="${sdk-folder}/android.jar" /> - - <!-- Rules --> - - <!-- Create the output directories if they don't exist yet. --> - <target name="dirs"> - <echo>Creating output directories if needed...</echo> - <mkdir dir="${outdir}" /> - <mkdir dir="${outdir-classes}" /> - </target> - - <!-- Generate the R.java file for this project's resources. --> - <target name="resource-src" depends="dirs"> - <echo>Generating R.java / Manifest.java from the resources...</echo> - <exec executable="${aapt}" failonerror="true"> - <arg value="package" /> - <arg value="-m" /> - <arg value="-J" /> - <arg value="${outdir-r}" /> - <arg value="-M" /> - <arg value="AndroidManifest.xml" /> - <arg value="-S" /> - <arg value="${resource-dir}" /> - <arg value="-I" /> - <arg value="${android-jar}" /> - </exec> - </target> - - <!-- Generate java classes from .aidl files. --> - <target name="aidl" depends="dirs"> - <echo>Compiling aidl files into Java classes...</echo> - <apply executable="${aidl}" failonerror="true"> - <arg value="-p${android-framework}" /> - <arg value="-I${srcdir}" /> - <fileset dir="${srcdir}"> - <include name="**/*.aidl"/> - </fileset> - </apply> - </target> - - <!-- Compile this project's .java files into .class files. --> - <target name="compile" depends="dirs, resource-src, aidl"> - <javac encoding="ascii" target="1.5" debug="true" extdirs="" - srcdir="." - destdir="${outdir-classes}" - bootclasspath="${android-jar}"> - <classpath> - <fileset dir="${external-libs}" includes="*.jar"/> - </classpath> - </javac> - </target> - - <!-- Convert this project's .class files into .dex files. --> - <target name="dex" depends="compile"> - <echo>Converting compiled files and external libraries into ${outdir}/${dex-file}...</echo> - <apply executable="${dx}" failonerror="true" parallel="true"> - <arg value="--dex" /> - <arg value="--output=${intermediate-dex-ospath}" /> - <arg path="${outdir-classes-ospath}" /> - <fileset dir="${external-libs}" includes="*.jar"/> - </apply> - </target> - - <!-- Put the project's resources into the output package file. --> - <target name="package-res-and-assets"> - <echo>Packaging resources and assets...</echo> - <exec executable="${aapt}" failonerror="true"> - <arg value="package" /> - <arg value="-f" /> - <arg value="-M" /> - <arg value="AndroidManifest.xml" /> - <arg value="-S" /> - <arg value="${resource-dir}" /> - <arg value="-A" /> - <arg value="${asset-dir}" /> - <arg value="-I" /> - <arg value="${android-jar}" /> - <arg value="-F" /> - <arg value="${resources-package}" /> - </exec> - </target> - - <!-- Same as package-res-and-assets, but without "-A ${asset-dir}" --> - <target name="package-res-no-assets"> - <echo>Packaging resources...</echo> - <exec executable="${aapt}" failonerror="true"> - <arg value="package" /> - <arg value="-f" /> - <arg value="-M" /> - <arg value="AndroidManifest.xml" /> - <arg value="-S" /> - <arg value="${resource-dir}" /> - <!-- No assets directory --> - <arg value="-I" /> - <arg value="${android-jar}" /> - <arg value="-F" /> - <arg value="${resources-package}" /> - </exec> - </target> - - <!-- Invoke the proper target depending on whether or not - an assets directory is present. --> - <!-- TODO: find a nicer way to include the "-A ${asset-dir}" argument - only when the assets dir exists. --> - <target name="package-res"> - <available file="${asset-dir}" type="dir" - property="res-target" value="and-assets" /> - <property name="res-target" value="no-assets" /> - <antcall target="package-res-${res-target}" /> - </target> - - <!-- Package the application and sign it with a debug key. - This is the default target when building. It is used for debug. --> - <target name="debug" depends="dex, package-res"> - <echo>Packaging ${out-debug-package}, and signing it with a debug key...</echo> - <exec executable="${apk-builder}" failonerror="true"> - <arg value="${out-debug-package-ospath}" /> - <arg value="-z" /> - <arg value="${resources-package-ospath}" /> - <arg value="-f" /> - <arg value="${intermediate-dex-ospath}" /> - <arg value="-rf" /> - <arg value="${srcdir-ospath}" /> - <arg value="-rj" /> - <arg value="${external-libs-ospath}" /> - <arg value="-nf" /> - <arg value="${native-libs-ospath}" /> - </exec> - </target> - - <!-- Package the application without signing it. - This allows for the application to be signed later with an official publishing key. --> - <target name="release" depends="dex, package-res"> - <echo>Packaging ${out-unsigned-package} for release...</echo> - <exec executable="${apk-builder}" failonerror="true"> - <arg value="${out-unsigned-package-ospath}" /> - <arg value="-u" /> - <arg value="-z" /> - <arg value="${resources-package-ospath}" /> - <arg value="-f" /> - <arg value="${intermediate-dex-ospath}" /> - <arg value="-rf" /> - <arg value="${srcdir-ospath}" /> - <arg value="-rj" /> - <arg value="${external-libs-ospath}" /> - <arg value="-nf" /> - <arg value="${native-libs-ospath}" /> - </exec> - <echo>It will need to be signed with jarsigner before being published.</echo> - </target> - - <!-- Install the package on the default emulator --> - <target name="install" depends="debug"> - <echo>Installing ${out-debug-package} onto default emulator...</echo> - <exec executable="${adb}" failonerror="true"> - <arg value="install" /> - <arg value="${out-debug-package}" /> - </exec> - </target> - - <target name="reinstall" depends="debug"> - <echo>Installing ${out-debug-package} onto default emulator...</echo> - <exec executable="${adb}" failonerror="true"> - <arg value="install" /> - <arg value="-r" /> - <arg value="${out-debug-package}" /> - </exec> - </target> - - <!-- Uinstall the package from the default emulator --> - <target name="uninstall"> - <echo>Uninstalling ${application-package} from the default emulator...</echo> - <exec executable="${adb}" failonerror="true"> - <arg value="uninstall" /> - <arg value="${application-package}" /> - </exec> - </target> + <import file="${sdk-folder}/tools/lib/android_rules.xml" /> </project> diff --git a/scripts/combine_sdks.sh b/scripts/combine_sdks.sh new file mode 100755 index 0000000..89a1141 --- /dev/null +++ b/scripts/combine_sdks.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +function replace() +{ + echo replacing $1 + rm -rf $UNZIPPED_BASE_DIR/$1 + cp -rf $UNZIPPED_IMAGE_DIR/$1 $UNZIPPED_BASE_DIR/$1 +} + +BASE=$1 +IMAGES=$2 +OUTPUT=$3 + +if [[ -z $BASE || -z $IMAGES || -z $OUTPUT ]] ; then + echo "usage: combine_sdks.sh BASE IMAGES OUTPUT" + echo + echo " BASE and IMAGES should be sdk zip files. The system image files," + echo " emulator and other runtime files will be copied from IMAGES and" + echo " everything else will be copied from BASE. All of this will be" + echo " bundled into OUTPUT and zipped up again." + echo + exit 1 +fi + +TMP=$(mktemp -d) + +TMP_ZIP=tmp.zip + +BASE_DIR=$TMP/base +IMAGES_DIR=$TMP/images +OUTPUT_TMP_ZIP=$BASE_DIR/$TMP_ZIP + +unzip -q $BASE -d $BASE_DIR +unzip -q $IMAGES -d $IMAGES_DIR + +UNZIPPED_BASE_DIR=$(echo $BASE_DIR/*) +UNZIPPED_IMAGE_DIR=$(echo $IMAGES_DIR/*) + +# +# The commands to copy over the files that we want +# + +# replace tools/emulator # at this time we do not want the exe from SDK1.x +replace tools/lib/images +replace docs +replace android.jar + +# +# end +# + +pushd $BASE_DIR &> /dev/null + # rename the directory to the leaf minus the .zip of OUTPUT + LEAF=$(echo $OUTPUT | sed -e "s:.*\.zip/::" | sed -e "s:.zip$::") + mv * $LEAF + # zip it + zip -qr $TMP_ZIP $LEAF +popd &> /dev/null + +cp $OUTPUT_TMP_ZIP $OUTPUT + +rm -rf $TMP diff --git a/scripts/default.properties.template b/scripts/default.properties.template index 5d708f2..63df494 100644 --- a/scripts/default.properties.template +++ b/scripts/default.properties.template @@ -2,4 +2,17 @@ # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # Instead customize values in a "build.properties" file. +# location of the SDK sdk-folder=ANDROID_SDK_FOLDER + +# target mode. Value can be "platform" or "add-on" +target-mode=TARGET_MODE + +# target API level. +target-api=TARGET_API + +# target name, if target-mode=add-on +target-name=TARGET_NAME + +# target platform. This is either the target itself or the platform the add-on is based on. +target-folder=TARGET_FOLDER diff --git a/scripts/divide_and_compress.py b/scripts/divide_and_compress.py new file mode 100755 index 0000000..d369be4 --- /dev/null +++ b/scripts/divide_and_compress.py @@ -0,0 +1,352 @@ +#!/usr/bin/python2.4 +# +# Copyright (C) 2008 Google Inc. +# +# 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. +# + +"""Module to compress directories in to series of zip files. + +This module will take a directory and compress all its contents, including +child directories into a series of zip files named N.zip where 'N' ranges from +0 to infinity. The zip files will all be below a certain specified maximum +threshold. + +The directory is compressed with a depth first traversal, each directory's +file contents being compressed as it is visisted, before the compression of any +child directory's contents. In this way the files within an archive are ordered +and the archives themselves are ordered. + +The class also constructs a 'main.py' file intended for use with Google App +Engine with a custom App Engine program not currently distributed with this +code base. The custom App Engine runtime can leverage the index files written +out by this class to more quickly locate which zip file to serve a given URL +from. +""" + +__author__ = 'jmatt@google.com (Justin Mattson)' + +from optparse import OptionParser +import os +import stat +import sys +import zipfile +from zipfile import ZipFile +import divide_and_compress_constants + + +def Main(argv): + parser = CreateOptionsParser() + (options, args) = parser.parse_args() + VerifyArguments(options, parser) + zipper = DirectoryZipper(options.destination, + options.sourcefiles, + ParseSize(options.filesize), + options.compress) + zipper.StartCompress() + + +def CreateOptionsParser(): + rtn = OptionParser() + rtn.add_option('-s', '--sourcefiles', dest='sourcefiles', default=None, + help='The directory containing the files to compress') + rtn.add_option('-d', '--destination', dest='destination', default=None, + help=('Where to put the archive files, this should not be' + ' a child of where the source files exist.')) + rtn.add_option('-f', '--filesize', dest='filesize', default='1M', + help=('Maximum size of archive files. A number followed by' + 'a magnitude indicator, eg. 1000000B == one million ' + 'BYTES, 500K == five hundred KILOBYTES, 1.2M == one ' + 'point two MEGABYTES. 1M == 1048576 BYTES')) + rtn.add_option('-n', '--nocompress', action='store_false', dest='compress', + default=True, + help=('Whether the archive files should be compressed, or ' + 'just a concatenation of the source files')) + return rtn + + +def VerifyArguments(options, parser): + try: + if options.sourcefiles is None or options.destination is None: + parser.print_help() + sys.exit(-1) + except (AttributeError), err: + parser.print_help() + sys.exit(-1) + + +def ParseSize(size_str): + if len(size_str) < 2: + raise ValueError(('filesize argument not understood, please include' + ' a numeric value and magnitude indicator')) + magnitude = size_str[len(size_str)-1:] + if not magnitude in ('K', 'B', 'M'): + raise ValueError(('filesize magnitude indicator not valid, must be \'K\',' + '\'B\', or \'M\'')) + numeral = float(size_str[0:len(size_str)-1]) + if magnitude == 'K': + numeral *= 1024 + elif magnitude == 'M': + numeral *= 1048576 + return int(numeral) + + +class DirectoryZipper(object): + """Class to compress a directory and all its sub-directories.""" + current_archive = None + output_dir = None + base_path = None + max_size = None + compress = None + index_fp = None + + def __init__(self, output_path, base_dir, archive_size, enable_compression): + """DirectoryZipper constructor. + + Args: + output_path: the path to write the archives and index file to + base_dir: the directory to compress + archive_size: the maximum size, in bytes, of a single archive file + enable_compression: whether or not compression should be enabled, if + disabled, the files will be written into an uncompresed zip + """ + self.output_dir = output_path + self.current_archive = '0.zip' + self.base_path = base_dir + self.max_size = archive_size + self.compress = enable_compression + + def StartCompress(self): + """Start compress of the directory. + + This will start the compression process and write the archives to the + specified output directory. It will also produce an 'index.txt' file in the + output directory that maps from file to archive. + """ + self.index_fp = open(''.join([self.output_dir, 'main.py']), 'w') + self.index_fp.write(divide_and_compress_constants.file_preamble) + os.path.walk(self.base_path, self.CompressDirectory, 1) + self.index_fp.write(divide_and_compress_constants.file_endpiece) + self.index_fp.close() + + def RemoveLastFile(self, archive_path=None): + """Removes the last item in the archive. + + This removes the last item in the archive by reading the items out of the + archive, adding them to a new archive, deleting the old archive, and + moving the new archive to the location of the old archive. + + Args: + archive_path: Path to the archive to modify. This archive should not be + open elsewhere, since it will need to be deleted. + Return: + A new ZipFile object that points to the modified archive file + """ + if archive_path is None: + archive_path = ''.join([self.output_dir, self.current_archive]) + + # Move the old file and create a new one at its old location + ext_offset = archive_path.rfind('.') + old_archive = ''.join([archive_path[0:ext_offset], '-old', + archive_path[ext_offset:]]) + os.rename(archive_path, old_archive) + old_fp = self.OpenZipFileAtPath(old_archive, mode='r') + + if self.compress: + new_fp = self.OpenZipFileAtPath(archive_path, + mode='w', + compress=zipfile.ZIP_DEFLATED) + else: + new_fp = self.OpenZipFileAtPath(archive_path, + mode='w', + compress=zipfile.ZIP_STORED) + + # Read the old archive in a new archive, except the last one + zip_members = enumerate(old_fp.infolist()) + num_members = len(old_fp.infolist()) + while num_members > 1: + this_member = zip_members.next()[1] + new_fp.writestr(this_member.filename, old_fp.read(this_member.filename)) + num_members -= 1 + + # Close files and delete the old one + old_fp.close() + new_fp.close() + os.unlink(old_archive) + + def OpenZipFileAtPath(self, path, mode=None, compress=zipfile.ZIP_DEFLATED): + """This method is mainly for testing purposes, eg dependency injection.""" + if mode is None: + if os.path.exists(path): + mode = 'a' + else: + mode = 'w' + + if mode == 'r': + return ZipFile(path, mode) + else: + return ZipFile(path, mode, compress) + + def CompressDirectory(self, irrelevant, dir_path, dir_contents): + """Method to compress the given directory. + + This method compresses the directory 'dir_path'. It will add to an existing + zip file that still has space and create new ones as necessary to keep zip + file sizes under the maximum specified size. This also writes out the + mapping of files to archives to the self.index_fp file descriptor + + Args: + irrelevant: a numeric identifier passed by the os.path.walk method, this + is not used by this method + dir_path: the path to the directory to compress + dir_contents: a list of directory contents to be compressed + """ + + # construct the queue of files to be added that this method will use + # it seems that dir_contents is given in reverse alphabetical order, + # so put them in alphabetical order by inserting to front of the list + dir_contents.sort() + zip_queue = [] + if dir_path[len(dir_path) - 1:] == os.sep: + for filename in dir_contents: + zip_queue.append(''.join([dir_path, filename])) + else: + for filename in dir_contents: + zip_queue.append(''.join([dir_path, os.sep, filename])) + compress_bit = zipfile.ZIP_DEFLATED + if not self.compress: + compress_bit = zipfile.ZIP_STORED + + # zip all files in this directory, adding to existing archives and creating + # as necessary + while len(zip_queue) > 0: + target_file = zip_queue[0] + if os.path.isfile(target_file): + self.AddFileToArchive(target_file, compress_bit) + + # see if adding the new file made our archive too large + if not self.ArchiveIsValid(): + + # IF fixing fails, the last added file was to large, skip it + # ELSE the current archive filled normally, make a new one and try + # adding the file again + if not self.FixArchive('SIZE'): + zip_queue.pop(0) + else: + self.current_archive = '%i.zip' % ( + int(self.current_archive[ + 0:self.current_archive.rfind('.zip')]) + 1) + else: + + # if this the first file in the archive, write an index record + self.WriteIndexRecord() + zip_queue.pop(0) + else: + zip_queue.pop(0) + + def WriteIndexRecord(self): + """Write an index record to the index file. + + Only write an index record if this is the first file to go into archive + + Returns: + True if an archive record is written, False if it isn't + """ + archive = self.OpenZipFileAtPath( + ''.join([self.output_dir, self.current_archive]), 'r') + archive_index = archive.infolist() + if len(archive_index) == 1: + self.index_fp.write( + '[\'%s\', \'%s\'],\n' % (self.current_archive, + archive_index[0].filename)) + archive.close() + return True + else: + archive.close() + return False + + def FixArchive(self, problem): + """Make the archive compliant. + + Args: + problem: the reason the archive is invalid + + Returns: + Whether the file(s) removed to fix the archive could conceivably be + in an archive, but for some reason can't be added to this one. + """ + archive_path = ''.join([self.output_dir, self.current_archive]) + rtn_value = None + + if problem == 'SIZE': + archive_obj = self.OpenZipFileAtPath(archive_path, mode='r') + num_archive_files = len(archive_obj.infolist()) + + # IF there is a single file, that means its too large to compress, + # delete the created archive + # ELSE do normal finalization + if num_archive_files == 1: + print ('WARNING: %s%s is too large to store.' % ( + self.base_path, archive_obj.infolist()[0].filename)) + archive_obj.close() + os.unlink(archive_path) + rtn_value = False + else: + self.RemoveLastFile(''.join([self.output_dir, self.current_archive])) + archive_obj.close() + print 'Final archive size for %s is %i' % ( + self.current_archive, os.stat(archive_path)[stat.ST_SIZE]) + rtn_value = True + return rtn_value + + def AddFileToArchive(self, filepath, compress_bit): + """Add the file at filepath to the current archive. + + Args: + filepath: the path of the file to add + compress_bit: whether or not this fiel should be compressed when added + + Returns: + True if the file could be added (typically because this is a file) or + False if it couldn't be added (typically because its a directory) + """ + curr_archive_path = ''.join([self.output_dir, self.current_archive]) + if os.path.isfile(filepath): + if os.stat(filepath)[stat.ST_SIZE] > 1048576: + print 'Warning: %s is potentially too large to serve on GAE' % filepath + archive = self.OpenZipFileAtPath(curr_archive_path, + compress=compress_bit) + # add the file to the archive + archive.write(filepath, filepath[len(self.base_path):]) + archive.close() + return True + else: + return False + + def ArchiveIsValid(self): + """Check whether the archive is valid. + + Currently this only checks whether the archive is under the required size. + The thought is that eventually this will do additional validation + + Returns: + True if the archive is valid, False if its not + """ + archive_path = ''.join([self.output_dir, self.current_archive]) + if os.stat(archive_path)[stat.ST_SIZE] > self.max_size: + return False + else: + return True + +if __name__ == '__main__': + Main(sys.argv) diff --git a/scripts/divide_and_compress_constants.py b/scripts/divide_and_compress_constants.py new file mode 100644 index 0000000..4e11b6f --- /dev/null +++ b/scripts/divide_and_compress_constants.py @@ -0,0 +1,60 @@ +#!/usr/bin/python2.4 +# +# Copyright (C) 2008 Google Inc. +# +# 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. +# + +"""Constants for the divide_and_compress script and DirectoryZipper class.""" + +__author__ = 'jmatt@google.com (Justin Mattson)' + +file_preamble = ('#!/usr/bin/env python\n' + '#\n' + '# Copyright 2008 Google Inc.\n' + '#\n' + '# Licensed under the Apache License, Version 2.0 (the' + '\"License");\n' + '# you may not use this file except in compliance with the ' + 'License.\n' + '# You may obtain a copy of the License at\n' + '#\n' + '# http://www.apache.org/licenses/LICENSE-2.0\n' + '#\n' + '# Unless required by applicable law or agreed to in writing,' + ' software\n' + '# distributed under the License is distributed on an \"AS' + 'IS\" BASIS,\n' + '# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ' + 'express or implied.\n' + '# See the License for the specific language governing' + ' permissions and\n' + '# limitations under the License.\n' + '#\n\n' + 'import wsgiref.handlers\n' + 'from google.appengine.ext import zipserve\n' + 'from google.appengine.ext import webapp\n' + 'import memcache_zipserve\n\n\n' + 'class MainHandler(webapp.RequestHandler):\n\n' + ' def get(self):\n' + ' self.response.out.write(\'Hello world!\')\n\n' + 'def main():\n' + ' application = webapp.WSGIApplication([(\'/(.*)\',' + ' memcache_zipserve.create_handler([') + +file_endpiece = ('])),\n' + '],\n' + 'debug=False)\n' + ' wsgiref.handlers.CGIHandler().run(application)\n\n' + 'if __name__ == \'__main__\':\n' + ' main()') diff --git a/scripts/java_tests_file.template b/scripts/java_tests_file.template new file mode 100644 index 0000000..7781a33 --- /dev/null +++ b/scripts/java_tests_file.template @@ -0,0 +1,21 @@ +package PACKAGE; + +import android.test.ActivityInstrumentationTestCase; + +/** + * This is a simple framework for a test of an Application. See + * {@link android.test.ApplicationTestCase ApplicationTestCase} for more information on + * how to write and extend Application tests. + * <p/> + * To run this test, you can type: + * adb shell am instrument -w \ + * -e class PACKAGE.ACTIVITY_NAMETest \ + * PACKAGE.tests/android.test.InstrumentationTestRunner + */ +public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase<ACTIVITY_NAME> { + + public ACTIVITY_NAMETest() { + super("PACKAGE", ACTIVITY_NAME.class); + } + +}
\ No newline at end of file diff --git a/scripts/plugin.prop b/scripts/plugin.prop index 15593ef..99dba4a 100644 --- a/scripts/plugin.prop +++ b/scripts/plugin.prop @@ -1,4 +1,4 @@ # begin plugin.prop -plugin.version=0.8.0 +plugin.version=0.9.0 plugin.platform=android # end plugin.prop
\ No newline at end of file diff --git a/scripts/test_divide_and_compress.py b/scripts/test_divide_and_compress.py new file mode 100755 index 0000000..d0d27b3 --- /dev/null +++ b/scripts/test_divide_and_compress.py @@ -0,0 +1,490 @@ +#!/usr/bin/python2.4 +# +# Copyright (C) 2008 Google Inc. +# +# 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. +# + +"""Tests for divide_and_compress.py. + +TODO: Add tests for module methods. +""" + +__author__ = 'jmatt@google.com (Justin Mattson)' + +import os +import stat +import unittest +import zipfile +from zipfile import ZipFile + +import divide_and_compress +from mox import mox + + +class BagOfParts(object): + """Just a generic class that I can use to assign random attributes to.""" + + def NoOp(self): + x = 1 + + +class ValidAndRemoveTests(unittest.TestCase): + """Test the ArchiveIsValid and RemoveLastFile methods.""" + + def setUp(self): + """Prepare the test. + + Construct some mock objects for use with the tests. + """ + self.my_mox = mox.Mox() + file1 = BagOfParts() + file1.filename = 'file1.txt' + file1.contents = 'This is a test file' + file2 = BagOfParts() + file2.filename = 'file2.txt' + file2.contents = ('akdjfk;djsf;kljdslkfjslkdfjlsfjkdvn;kn;2389rtu4i' + 'tn;ghf8:89H*hp748FJw80fu9WJFpwf39pujens;fihkhjfk' + 'sdjfljkgsc n;iself') + self.files = {'file1': file1, 'file2': file2} + + def testArchiveIsValid(self): + """Test the DirectoryZipper.ArchiveIsValid method. + + Run two tests, one that we expect to pass and one that we expect to fail + """ + test_file_size = 1056730 + self.my_mox.StubOutWithMock(os, 'stat') + os.stat('/foo/0.zip').AndReturn([test_file_size]) + self.my_mox.StubOutWithMock(stat, 'ST_SIZE') + stat.ST_SIZE = 0 + os.stat('/baz/0.zip').AndReturn([test_file_size]) + mox.Replay(os.stat) + test_target = divide_and_compress.DirectoryZipper('/foo/', 'bar', + test_file_size - 1, True) + + self.assertEqual(False, test_target.ArchiveIsValid(), + msg=('ERROR: Test failed, ArchiveIsValid should have ' + 'returned false, but returned true')) + + test_target = divide_and_compress.DirectoryZipper('/baz/', 'bar', + test_file_size + 1, True) + self.assertEqual(True, test_target.ArchiveIsValid(), + msg=('ERROR: Test failed, ArchiveIsValid should have' + ' returned true, but returned false')) + + def testRemoveLastFile(self): + """Test DirectoryZipper.RemoveLastFile method. + + Construct a ZipInfo mock object with two records, verify that write is + only called once on the new ZipFile object. + """ + source = self.CreateZipSource() + dest = self.CreateZipDestination() + source_path = ''.join([os.getcwd(), '/0-old.zip']) + dest_path = ''.join([os.getcwd(), '/0.zip']) + test_target = divide_and_compress.DirectoryZipper( + ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True) + self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') + test_target.OpenZipFileAtPath(source_path, mode='r').AndReturn(source) + test_target.OpenZipFileAtPath(dest_path, + compress=zipfile.ZIP_DEFLATED, + mode='w').AndReturn(dest) + self.my_mox.StubOutWithMock(os, 'rename') + os.rename(dest_path, source_path) + self.my_mox.StubOutWithMock(os, 'unlink') + os.unlink(source_path) + + self.my_mox.ReplayAll() + test_target.RemoveLastFile() + self.my_mox.VerifyAll() + + def CreateZipSource(self): + """Create a mock zip sourec object. + + Read should only be called once, because the second file is the one + being removed. + + Returns: + A configured mocked + """ + + source_zip = self.my_mox.CreateMock(ZipFile) + source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']]) + source_zip.infolist().AndReturn([self.files['file1'], self.files['file1']]) + source_zip.read(self.files['file1'].filename).AndReturn( + self.files['file1'].contents) + source_zip.close() + return source_zip + + def CreateZipDestination(self): + """Create mock destination zip. + + Write should only be called once, because there are two files in the + source zip and we expect the second to be removed. + + Returns: + A configured mocked + """ + + dest_zip = mox.MockObject(ZipFile) + dest_zip.writestr(self.files['file1'].filename, + self.files['file1'].contents) + dest_zip.close() + return dest_zip + + def tearDown(self): + """Remove any stubs we've created.""" + self.my_mox.UnsetStubs() + + +class FixArchiveTests(unittest.TestCase): + """Tests for the DirectoryZipper.FixArchive method.""" + + def setUp(self): + """Create a mock file object.""" + self.my_mox = mox.Mox() + self.file1 = BagOfParts() + self.file1.filename = 'file1.txt' + self.file1.contents = 'This is a test file' + + def _InitMultiFileData(self): + """Create an array of mock file objects. + + Create three mock file objects that we can use for testing. + """ + self.multi_file_dir = [] + + file1 = BagOfParts() + file1.filename = 'file1.txt' + file1.contents = 'kjaskl;jkdjfkja;kjsnbvjnvnbuewklriujalvjsd' + self.multi_file_dir.append(file1) + + file2 = BagOfParts() + file2.filename = 'file2.txt' + file2.contents = ('He entered the room and there in the center, it was.' + ' Looking upon the thing, suddenly he could not remember' + ' whether he had actually seen it before or whether' + ' his memory of it was merely the effect of something' + ' so often being imagined that it had long since become ' + ' manifest in his mind.') + self.multi_file_dir.append(file2) + + file3 = BagOfParts() + file3.filename = 'file3.txt' + file3.contents = 'Whoa, what is \'file2.txt\' all about?' + self.multi_file_dir.append(file3) + + def testSingleFileArchive(self): + """Test behavior of FixArchive when the archive has a single member. + + We expect that when this method is called with an archive that has a + single member that it will return False and unlink the archive. + """ + test_target = divide_and_compress.DirectoryZipper( + ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True) + self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') + test_target.OpenZipFileAtPath( + ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn( + self.CreateSingleFileMock()) + self.my_mox.StubOutWithMock(os, 'unlink') + os.unlink(''.join([os.getcwd(), '/0.zip'])) + self.my_mox.ReplayAll() + self.assertEqual(False, test_target.FixArchive('SIZE')) + self.my_mox.VerifyAll() + + def CreateSingleFileMock(self): + """Create a mock ZipFile object for testSingleFileArchive. + + We just need it to return a single member infolist twice + + Returns: + A configured mock object + """ + mock_zip = self.my_mox.CreateMock(ZipFile) + mock_zip.infolist().AndReturn([self.file1]) + mock_zip.infolist().AndReturn([self.file1]) + mock_zip.close() + return mock_zip + + def testMultiFileArchive(self): + """Test behavior of DirectoryZipper.FixArchive with a multi-file archive. + + We expect that FixArchive will rename the old archive, adding '-old' before + '.zip', read all the members except the last one of '-old' into a new + archive with the same name as the original, and then unlink the '-old' copy + """ + test_target = divide_and_compress.DirectoryZipper( + ''.join([os.getcwd(), '/']), 'dummy', 1024*1024, True) + self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') + test_target.OpenZipFileAtPath( + ''.join([os.getcwd(), '/0.zip']), mode='r').AndReturn( + self.CreateMultiFileMock()) + self.my_mox.StubOutWithMock(test_target, 'RemoveLastFile') + test_target.RemoveLastFile(''.join([os.getcwd(), '/0.zip'])) + self.my_mox.StubOutWithMock(os, 'stat') + os.stat(''.join([os.getcwd(), '/0.zip'])).AndReturn([49302]) + self.my_mox.StubOutWithMock(stat, 'ST_SIZE') + stat.ST_SIZE = 0 + self.my_mox.ReplayAll() + self.assertEqual(True, test_target.FixArchive('SIZE')) + self.my_mox.VerifyAll() + + def CreateMultiFileMock(self): + """Create mock ZipFile object for use with testMultiFileArchive. + + The mock just needs to return the infolist mock that is prepared in + InitMultiFileData() + + Returns: + A configured mock object + """ + self._InitMultiFileData() + mock_zip = self.my_mox.CreateMock(ZipFile) + mock_zip.infolist().AndReturn(self.multi_file_dir) + mock_zip.close() + return mock_zip + + def tearDown(self): + """Unset any mocks that we've created.""" + self.my_mox.UnsetStubs() + + +class AddFileToArchiveTest(unittest.TestCase): + """Test behavior of method to add a file to an archive.""" + + def setUp(self): + """Setup the arguments for the DirectoryZipper object.""" + self.my_mox = mox.Mox() + self.output_dir = '%s/' % os.getcwd() + self.file_to_add = 'file.txt' + self.input_dir = '/foo/bar/baz/' + + def testAddFileToArchive(self): + """Test the DirectoryZipper.AddFileToArchive method. + + We are testing a pretty trivial method, we just expect it to look at the + file its adding, so that it possible can through out a warning. + """ + test_target = divide_and_compress.DirectoryZipper(self.output_dir, + self.input_dir, + 1024*1024, True) + self.my_mox.StubOutWithMock(test_target, 'OpenZipFileAtPath') + archive_mock = self.CreateArchiveMock() + test_target.OpenZipFileAtPath( + ''.join([self.output_dir, '0.zip']), + compress=zipfile.ZIP_DEFLATED).AndReturn(archive_mock) + self.StubOutOsModule() + self.my_mox.ReplayAll() + test_target.AddFileToArchive(''.join([self.input_dir, self.file_to_add]), + zipfile.ZIP_DEFLATED) + self.my_mox.VerifyAll() + + def StubOutOsModule(self): + """Create a mock for the os.path and os.stat objects. + + Create a stub that will return the type (file or directory) and size of the + object that is to be added. + """ + self.my_mox.StubOutWithMock(os.path, 'isfile') + os.path.isfile(''.join([self.input_dir, self.file_to_add])).AndReturn(True) + self.my_mox.StubOutWithMock(os, 'stat') + os.stat(''.join([self.input_dir, self.file_to_add])).AndReturn([39480]) + self.my_mox.StubOutWithMock(stat, 'ST_SIZE') + stat.ST_SIZE = 0 + + def CreateArchiveMock(self): + """Create a mock ZipFile for use with testAddFileToArchive. + + Just verify that write is called with the file we expect and that the + archive is closed after the file addition + + Returns: + A configured mock object + """ + archive_mock = self.my_mox.CreateMock(ZipFile) + archive_mock.write(''.join([self.input_dir, self.file_to_add]), + self.file_to_add) + archive_mock.close() + return archive_mock + + def tearDown(self): + self.my_mox.UnsetStubs() + + +class CompressDirectoryTest(unittest.TestCase): + """Test the master method of the class. + + Testing with the following directory structure. + /dir1/ + /dir1/file1.txt + /dir1/file2.txt + /dir1/dir2/ + /dir1/dir2/dir3/ + /dir1/dir2/dir4/ + /dir1/dir2/dir4/file3.txt + /dir1/dir5/ + /dir1/dir5/file4.txt + /dir1/dir5/file5.txt + /dir1/dir5/file6.txt + /dir1/dir5/file7.txt + /dir1/dir6/ + /dir1/dir6/file8.txt + + file1.txt., file2.txt, file3.txt should be in 0.zip + file4.txt should be in 1.zip + file5.txt, file6.txt should be in 2.zip + file7.txt will not be stored since it will be too large compressed + file8.txt should b in 3.zip + """ + + def setUp(self): + """Setup all the mocks for this test.""" + self.my_mox = mox.Mox() + + self.base_dir = '/dir1' + self.output_path = '/out_dir/' + self.test_target = divide_and_compress.DirectoryZipper( + self.output_path, self.base_dir, 1024*1024, True) + + self.InitArgLists() + self.InitOsDotPath() + self.InitArchiveIsValid() + self.InitWriteIndexRecord() + self.InitAddFileToArchive() + + def tearDown(self): + self.my_mox.UnsetStubs() + + def testCompressDirectory(self): + """Test the DirectoryZipper.CompressDirectory method.""" + self.my_mox.ReplayAll() + for arguments in self.argument_lists: + self.test_target.CompressDirectory(None, arguments[0], arguments[1]) + self.my_mox.VerifyAll() + + def InitAddFileToArchive(self): + """Setup mock for DirectoryZipper.AddFileToArchive. + + Make sure that the files are added in the order we expect. + """ + self.my_mox.StubOutWithMock(self.test_target, 'AddFileToArchive') + self.test_target.AddFileToArchive('/dir1/file1.txt', zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/file2.txt', zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir2/dir4/file3.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file4.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file4.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file5.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file5.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file6.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file7.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir5/file7.txt', + zipfile.ZIP_DEFLATED) + self.test_target.AddFileToArchive('/dir1/dir6/file8.txt', + zipfile.ZIP_DEFLATED) + + def InitWriteIndexRecord(self): + """Setup mock for DirectoryZipper.WriteIndexRecord.""" + self.my_mox.StubOutWithMock(self.test_target, 'WriteIndexRecord') + + # we are trying to compress 8 files, but we should only attempt to + # write an index record 7 times, because one file is too large to be stored + self.test_target.WriteIndexRecord().AndReturn(True) + self.test_target.WriteIndexRecord().AndReturn(False) + self.test_target.WriteIndexRecord().AndReturn(False) + self.test_target.WriteIndexRecord().AndReturn(True) + self.test_target.WriteIndexRecord().AndReturn(True) + self.test_target.WriteIndexRecord().AndReturn(False) + self.test_target.WriteIndexRecord().AndReturn(True) + + def InitArchiveIsValid(self): + """Mock out DirectoryZipper.ArchiveIsValid and DirectoryZipper.FixArchive. + + Mock these methods out such that file1, file2, and file3 go into one + archive. file4 then goes into the next archive, file5 and file6 in the + next, file 7 should appear too large to compress into an archive, and + file8 goes into the final archive + """ + self.my_mox.StubOutWithMock(self.test_target, 'ArchiveIsValid') + self.my_mox.StubOutWithMock(self.test_target, 'FixArchive') + self.test_target.ArchiveIsValid().AndReturn(True) + self.test_target.ArchiveIsValid().AndReturn(True) + self.test_target.ArchiveIsValid().AndReturn(True) + + # should be file4.txt + self.test_target.ArchiveIsValid().AndReturn(False) + self.test_target.FixArchive('SIZE').AndReturn(True) + self.test_target.ArchiveIsValid().AndReturn(True) + + # should be file5.txt + self.test_target.ArchiveIsValid().AndReturn(False) + self.test_target.FixArchive('SIZE').AndReturn(True) + self.test_target.ArchiveIsValid().AndReturn(True) + self.test_target.ArchiveIsValid().AndReturn(True) + + # should be file7.txt + self.test_target.ArchiveIsValid().AndReturn(False) + self.test_target.FixArchive('SIZE').AndReturn(True) + self.test_target.ArchiveIsValid().AndReturn(False) + self.test_target.FixArchive('SIZE').AndReturn(False) + self.test_target.ArchiveIsValid().AndReturn(True) + + def InitOsDotPath(self): + """Mock out os.path.isfile. + + Mock this out so the things we want to appear as files appear as files and + the things we want to appear as directories appear as directories. Also + make sure that the order of file visits is as we expect (which is why + InAnyOrder isn't used here). + """ + self.my_mox.StubOutWithMock(os.path, 'isfile') + os.path.isfile('/dir1/dir2').AndReturn(False) + os.path.isfile('/dir1/dir5').AndReturn(False) + os.path.isfile('/dir1/dir6').AndReturn(False) + os.path.isfile('/dir1/file1.txt').AndReturn(True) + os.path.isfile('/dir1/file2.txt').AndReturn(True) + os.path.isfile('/dir1/dir2/dir3').AndReturn(False) + os.path.isfile('/dir1/dir2/dir4').AndReturn(False) + os.path.isfile('/dir1/dir2/dir4/file3.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file4.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file5.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file6.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True) + os.path.isfile('/dir1/dir5/file7.txt').AndReturn(True) + os.path.isfile('/dir1/dir6/file8.txt').AndReturn(True) + + def InitArgLists(self): + """Create the directory path => directory contents mappings.""" + self.argument_lists = [] + self.argument_lists.append(['/dir1', + ['file1.txt', 'file2.txt', 'dir2', 'dir5', + 'dir6']]) + self.argument_lists.append(['/dir1/dir2', ['dir3', 'dir4']]) + self.argument_lists.append(['/dir1/dir2/dir3', []]) + self.argument_lists.append(['/dir1/dir2/dir4', ['file3.txt']]) + self.argument_lists.append(['/dir1/dir5', + ['file4.txt', 'file5.txt', 'file6.txt', + 'file7.txt']]) + self.argument_lists.append(['/dir1/dir6', ['file8.txt']]) + +if __name__ == '__main__': + unittest.main() |