aboutsummaryrefslogtreecommitdiffhomepage
path: root/third_party/ijar/test/ijar_test.sh
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/ijar/test/ijar_test.sh')
-rwxr-xr-xthird_party/ijar/test/ijar_test.sh417
1 files changed, 417 insertions, 0 deletions
diff --git a/third_party/ijar/test/ijar_test.sh b/third_party/ijar/test/ijar_test.sh
new file mode 100755
index 0000000000..9869969adf
--- /dev/null
+++ b/third_party/ijar/test/ijar_test.sh
@@ -0,0 +1,417 @@
+#!/bin/bash -eu
+#
+# Copyright 2015 Google Inc. All rights reserved.
+# 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
+
+# TODO(bazel-team): this file is bloated, we should factor it.
+
+#### Inputs
+
+## TEST_TMPDIR
+if [ -z "${TEST_TMPDIR:-}" ]; then
+ TEST_TMPDIR="$(mktemp -d ${TMPDIR:-/tmp}/ijar-test.XXXXXXXX)"
+ trap "rm -fr ${TEST_TMPDIR}" EXIT
+fi
+
+## Mac OS X stat and MD5
+PLATFORM="$(uname -s | tr 'A-Z' 'a-z')"
+if [[ "$PLATFORM" = "darwin" ]]; then
+ function statfmt() {
+ stat -f "%z" $1
+ }
+ MD5SUM=/sbin/md5
+else
+ function statfmt() {
+ stat -c "%s" $1
+ }
+ MD5SUM=md5sum
+fi
+
+## Tools
+JAVAC=$1
+JAVA=$2
+JAR=$3
+JAVAP=$4
+IJAR=$TEST_SRCDIR/$5
+LANGTOOLS8=$TEST_SRCDIR/$6
+UNZIP=$7
+ZIP=$8
+
+IJAR_SRCDIR=$(dirname ${IJAR})
+A_JAR=$TEST_TMPDIR/A.jar
+A_INTERFACE_JAR=$TEST_TMPDIR/A-interface.jar
+A_ZIP_JAR=$TEST_TMPDIR/A_zip.jar
+A_ZIP_INTERFACE_JAR=$TEST_TMPDIR/A_zip-interface.jar
+W_JAR=$TEST_TMPDIR/W.jar
+BOTTLES_JAR=$TEST_TMPDIR/bottles.jar
+JAR_WRONG_CENTRAL_DIR=$IJAR_SRCDIR/test/libwrongcentraldir.jar
+IJAR_WRONG_CENTRAL_DIR=$TEST_TMPDIR/wrongcentraldir_interface.jar
+OBJECT_JAVA=$IJAR_SRCDIR/test/Object.java
+OBJECT_JAR=$TEST_TMPDIR/object.jar
+OBJECT_IJAR=$TEST_TMPDIR/object_interface.jar
+TYPEANN2_JAR=$IJAR_SRCDIR/test/libtypeannotations2.jar
+TYPEANN2_IJAR=$TEST_TMPDIR/typeannotations2_interface.jar
+TYPEANN2_JAVA=$IJAR_SRCDIR/test/TypeAnnotationTest2.java
+INVOKEDYNAMIC_JAR=$IJAR_SRCDIR/test/libinvokedynamic.jar
+INVOKEDYNAMIC_IJAR=$TEST_TMPDIR/invokedynamic_interface.jar
+
+#### Testing framework
+# Print message in "$1" then exit with status "$2"
+die () {
+ # second argument is optional, defaulting to 1
+ local status_code=${2:-1}
+ # Stop capturing stdout/stderr, and dump captured output
+ if [ "$CAPTURED_STD_ERR" -ne 0 ]; then
+ restore_outputs
+ cat "${TEST_TMPDIR}/captured.err" 1>&2
+ fi
+
+ if [ -n "${1-}" ] ; then
+ echo "$1" 1>&2
+ fi
+ if [ x"$status_code" != x -a x"$status_code" != x"0" ]; then
+ exit "$status_code"
+ else
+ exit 1
+ fi
+}
+
+# Die if "$1" == "$2", print $3 as death reason
+check_ne () {
+ if [ "$1" = "$2" ]; then
+ die "Check failed: '$1' != '$2' ${3:+ ($3)}"
+ fi
+}
+
+# Die if "$1" != "$2", print $3 as death reason
+check_eq () {
+ if [ ! "$1" = "$2" ]; then
+ die "Check failed: '$1' = '$2' ${3:+ ($3)}"
+ fi
+}
+
+CAPTURED_STD_ERR="${CAPTURED_STD_ERR:-0}"
+
+capture_test_stderr () {
+ exec 6>&2 # Save stderr as fd 6
+ exec 7>"${TEST_TMPDIR}/captured.err"
+ exec 2>&7
+ CAPTURED_STD_ERR=1
+}
+
+restore_outputs () {
+ if [ "$CAPTURED_STD_ERR" -ne 0 ] ; then
+ exec 2>&6
+ fi
+}
+
+recapture_outputs () {
+ if [ "$CAPTURED_STD_ERR" -ne 0 ] ; then
+ exec 2>&7
+ fi
+}
+
+#### Setup
+
+# set_file_length FILE SIZE
+#
+# Sets the file size for FILE, truncating if necessary, creating a
+# sparse file if possible, preserving original contents if they fit.
+function set_file_length() {
+ perl -e 'open(FH, ">>$ARGV[0]") && truncate(FH, $ARGV[1]) or die $!' "$@" &&
+ [[ "$(statfmt $1)" == "$2" ]] ||
+ die "set_file_length failed"
+}
+
+# grep_test_stderr STRING MESSAGE
+#
+# Greps the captured stderr text for STRING. If not found, fails with MESSAGE.
+grep_test_stderr() {
+ restore_outputs
+ [ "$CAPTURED_STD_ERR" -ne 0 ] || \
+ die "Must call capture_test_stderr before grep_test_stderr"
+ grep -c "$1" ${TEST_TMPDIR}/captured.err >/dev/null || \
+ die "Check failed: grep -c '$1' ${TEST_TMPDIR}/captured.err ${2:+ ($2)}"
+ CAPTURED_STD_ERR=0
+ recapture_outputs
+}
+
+# check_consistent_file_contents FILE
+#
+# Checks that all files created with the given filename have identical contents.
+expected_output=""
+function check_consistent_file_contents() {
+ local actual="$(cat $1 | ${MD5SUM} | awk '{ print $1; }')"
+ local filename="$(echo $1 | ${MD5SUM} | awk '{ print $1; }')"
+ local expected="$actual"
+ if $(echo "${expected_output}" | grep -q "^${filename} "); then
+ echo "${expected_output}" | grep -q "^${filename} ${actual}$" || {
+ ls -l "$1"
+ die "output file contents differ"
+ }
+ else
+ expected_output="$expected_output$filename $actual
+"
+ fi
+}
+
+# Tests that ijar does not crash when output ijar is bigger than the input jar
+rm -fr $TEST_TMPDIR/classes
+mkdir -p $TEST_TMPDIR/classes || die "mkdir $TEST_TMPDIR/classes failed"
+$JAVAC -g -d $TEST_TMPDIR/classes \
+ $IJAR_SRCDIR/test/WellCompressed*.java ||
+ die "javac failed"
+$JAR cf $W_JAR -C $TEST_TMPDIR/classes . || die "jar failed"
+
+W_INTERFACE_JAR=$TEST_TMPDIR/W-interface.jar
+$IJAR $W_JAR $W_INTERFACE_JAR || die "ijar failed"
+
+mkdir -p $TEST_TMPDIR/java/lang
+cp $OBJECT_JAVA $TEST_TMPDIR/java/lang/.
+$JAVAC $TEST_TMPDIR/java/lang/Object.java || die "javac failed"
+$JAR cf $OBJECT_JAR -C $TEST_TMPDIR java/lang/Object.class || die "jar failed"
+
+# Tests that ijar can handle class bodies longer than 64K
+rm -fr $TEST_TMPDIR/classes
+mkdir -p $TEST_TMPDIR/classes || die "mkdir $TEST_TMPDIR/classes failed"
+# First, generate the input file
+BOTTLES_JAVA=$TEST_TMPDIR/BottlesOnTheWall.java
+echo "public class BottlesOnTheWall {" > $BOTTLES_JAVA
+for i in $(seq 1 16384); do
+ echo " public int getBottleOnTheWall${i}() { return ${i}; }" >> $BOTTLES_JAVA
+done
+
+echo "}" >> $BOTTLES_JAVA
+
+$JAVAC -g -d $TEST_TMPDIR/classes $BOTTLES_JAVA || die "javac failed"
+BOTTLES_INTERFACE_JAR=$TEST_TMPDIR/bottles-interface.jar
+
+for flag0 in '' '0'; do
+ $JAR c"${flag0}"f $BOTTLES_JAR -C $TEST_TMPDIR/classes . || die "jar failed"
+ $IJAR $BOTTLES_JAR $BOTTLES_INTERFACE_JAR || die "ijar failed"
+ check_consistent_file_contents $BOTTLES_INTERFACE_JAR
+done
+
+# Compiles A.java, builds A.jar and A-interface.jar
+rm -fr $TEST_TMPDIR/classes
+mkdir -p $TEST_TMPDIR/classes || die "mkdir $TEST_TMPDIR/classes failed"
+$JAVAC -g -d $TEST_TMPDIR/classes $IJAR_SRCDIR/test/A.java ||
+ die "javac failed"
+
+for flag0 in '' '0'; do
+# Ensure input files larger than INITIAL_BUFFER_SIZE work.
+# TODO(martinrb): remove maximum .class file size limit (MAX_BUFFER_SIZE)
+for size in '' $((1024*1024)) $((15*1024*1024)); do
+ if [[ -n "$size" ]]; then
+ for file in $(find $TEST_TMPDIR/classes -name '*.class'); do
+ set_file_length "$file" "$size"
+ done
+ fi
+ $JAR c"${flag0}"f $A_JAR -C $TEST_TMPDIR/classes . || die "jar failed"
+ $IJAR $A_JAR $A_INTERFACE_JAR || die "ijar failed."
+ check_consistent_file_contents $A_INTERFACE_JAR
+ done
+done
+
+# Creates a huge (3Gb) input jar to test "large file" correctness
+set_file_length $TEST_TMPDIR/zeroes.data $((3*1024*1024*1024))
+for flag0 in '' '0'; do
+ $JAR c"${flag0}"f $A_JAR -C $TEST_TMPDIR zeroes.data -C $TEST_TMPDIR/classes . || die "jar failed"
+ $IJAR $A_JAR $A_INTERFACE_JAR || die "ijar failed."
+ check_consistent_file_contents $A_INTERFACE_JAR
+done
+
+# Create an output jar with upper bound on size > 2GB
+DIR=$TEST_TMPDIR/ManyLargeClasses
+mkdir -p $DIR/classes
+for i in $(seq 200); do
+ printf "class C${i} {}\n" > $DIR/C${i}.java
+done
+([[ "$JAVAC" =~ ^/ ]] || JAVAC="$PWD/$JAVAC"; cd $DIR && $JAVAC -d classes *.java)
+for i in $(seq 200); do
+ set_file_length $DIR/classes/C${i}.class $((15*1024*1024))
+done
+$JAR cf $DIR/ManyLargeClasses.jar -C $DIR/classes . || die "jar failed"
+$IJAR $DIR/ManyLargeClasses.jar $DIR/ManyLargeClasses.ijar || die "ijar failed."
+
+#### Checks
+
+# Check that ijar can produce class files with a body longer than 64K by
+# calling ijar itself on the output file to make sure that it is valid
+BOTTLES_INTERFACE_INTERFACE_JAR=$TEST_TMPDIR/bottles-interface-interface.jar
+$IJAR $BOTTLES_INTERFACE_JAR $BOTTLES_INTERFACE_INTERFACE_JAR ||
+ die "ijar cannot produce class files with body longer than 64K"
+
+# Check that the interface jar is bigger than the original jar.
+W_JAR_SIZE=$(statfmt $W_JAR)
+W_INTERFACE_JAR_SIZE=$(statfmt $W_INTERFACE_JAR)
+[[ $W_INTERFACE_JAR_SIZE -gt $W_JAR_SIZE ]] || die "interface jar should be bigger"
+
+# Check that the number of entries is 5:
+# A, A.PrivateInner, A.PublicInner, A.MyAnnotation,
+# A.RuntimeAnnotation
+# (Note: even private inner classes are retained, so we don't need to change
+# the types of members.)
+lines=$($JAR tvf $A_INTERFACE_JAR | wc -l)
+expected=5
+check_eq $expected $lines "Interface jar should have $expected entries!"
+
+
+# Check that no private class members are found:
+lines=$($JAVAP -private -classpath $A_JAR A | grep priv | wc -l)
+check_eq 2 $lines "Input jar should have 2 private members!"
+lines=$($JAVAP -private -classpath $A_INTERFACE_JAR A | grep priv | wc -l)
+check_eq 0 $lines "Interface jar should have no private members!"
+lines=$($JAVAP -private -classpath $A_INTERFACE_JAR A | grep clinit | wc -l)
+check_eq 0 $lines "Interface jar should have no class initializers!"
+
+
+# Check that no code is found:
+lines=$($JAVAP -c -private -classpath $A_JAR A | grep Code: | wc -l)
+check_eq 5 $lines "Input jar should have 5 method bodies!"
+lines=$($JAVAP -c -private -classpath $A_INTERFACE_JAR A | grep Code: | wc -l)
+check_eq 0 $lines "Interface jar should have no method bodies!"
+
+
+# Check that constants from code are no longer present:
+$JAVAP -c -private -classpath $A_JAR A | grep -sq foofoofoofoo ||
+ die "Input jar should have code constants!"
+$JAVAP -c -private -classpath $A_INTERFACE_JAR A | grep -sq foofoofoofoo &&
+ die "Interface jar should have no code constants!"
+
+
+# Check (important, this!) that the interface jar is still sufficient
+# for compiling:
+$JAVAC -Xlint -classpath $A_INTERFACE_JAR -g -d $TEST_TMPDIR/classes \
+ $IJAR_SRCDIR/test/B.java 2>$TEST_TMPDIR/B.javac.err ||
+ { cat $TEST_TMPDIR/B.javac.err >&2; die "Can't compile B!"; }
+
+
+# Test compilation of B yielded deprecation message:
+grep -sq 'deprecatedMethod.*in A has been deprecated' \
+ $TEST_TMPDIR/B.javac.err || die "ijar has lost @Deprecated annotation!"
+
+
+# Check idempotence of ijar transformation:
+A_INTERFACE_INTERFACE_JAR=$TEST_TMPDIR/A-interface-interface.jar
+$IJAR $A_INTERFACE_JAR $A_INTERFACE_INTERFACE_JAR || die "ijar failed."
+cmp $A_INTERFACE_JAR $A_INTERFACE_INTERFACE_JAR ||
+ die "ijar transformation is not idempotent"
+
+
+# Check that -interface.jar contains nothing but .class files:
+check_eq 0 $($JAR tf $A_INTERFACE_JAR | grep -v \\.class$ | wc -l) \
+ "Interface jar should contain only .class files!"
+
+
+# Check that -interface.jar timestamps are all zeros:
+check_eq 0 $(TZ=UTC $JAR tvf $A_INTERFACE_JAR |
+ grep -v 'Fri Nov 30 00:00:00 UTC 1979' | wc -l) \
+ "Interface jar contained non-zero timestamps!"
+
+
+# Check that compile-time constants in A are still annotated as such in ijar:
+$JAVAP -classpath $TEST_TMPDIR/classes -c B | grep -sq ldc2_w.*123 ||
+ die "ConstantValue not propagated to class B!"
+
+# Regression test for jar file without classes (javac doesn't like an empty ijar).
+>$TEST_TMPDIR/empty
+$ZIP $TEST_TMPDIR/noclasses.jar $TEST_TMPDIR/empty >/dev/null 2>&1
+$IJAR $TEST_TMPDIR/noclasses.jar || die "ijar failed"
+$UNZIP -qql $TEST_TMPDIR/noclasses-interface.jar 2>/dev/null | grep -q . ||
+ die "noclasses-interface.jar is completely empty!"
+ $JAVAC -classpath $TEST_TMPDIR/noclasses-interface.jar \
+ -d $TEST_TMPDIR/classes \
+ $IJAR_SRCDIR/test/A.java ||
+ die "javac noclasses-interface.jar failed"
+rm $TEST_TMPDIR/{empty,noclasses.jar,noclasses-interface.jar}
+
+
+# Run the dynamic checks in B.main().
+$JAVA -classpath $TEST_TMPDIR/classes B || exit 1
+
+# TODO(bazel-team) test that modifying the source in a non-interface
+# changing way results in the same -interface.jar.
+
+# Check that a jar compressed with zip results in the same interface jar as a
+# jar compressed with jar
+rm -fr $TEST_TMPDIR/classes
+mkdir -p $TEST_TMPDIR/classes || die "mkdir $TEST_TMPDIR/classes failed"
+$JAVAC -g -d $TEST_TMPDIR/classes $IJAR_SRCDIR/test/A.java ||
+ die "javac failed"
+$JAR cf $A_JAR $TEST_TMPDIR/classes/A.class || die "jar failed"
+$ZIP $A_ZIP_JAR $TEST_TMPDIR/classes/A.class || die "zip failed"
+
+$IJAR $A_JAR $A_INTERFACE_JAR || die "ijar failed"
+$IJAR $A_ZIP_JAR $A_ZIP_INTERFACE_JAR || die "ijar failed"
+cmp $A_INTERFACE_JAR $A_ZIP_INTERFACE_JAR || \
+ die "ijars from jar and zip are different"
+
+
+# Check that a JAR file can be parsed even if the central directory file count
+# is wrong
+$IJAR $JAR_WRONG_CENTRAL_DIR $IJAR_WRONG_CENTRAL_DIR || die "ijar failed"
+IJAR_FILES=$($UNZIP -qql $IJAR_WRONG_CENTRAL_DIR | wc -l | xargs echo)
+if [[ $IJAR_FILES != 2 ]]; then
+ die "ijar removed files"
+fi
+
+# Check that constant pool references used by JSR308 type annotations are
+# preserved
+$IJAR $TYPEANN2_JAR $TYPEANN2_IJAR || die "ijar failed"
+$JAVAP -classpath $TYPEANN2_IJAR -v Util |
+ grep -sq RuntimeVisibleTypeAnnotations ||
+ die "RuntimeVisibleTypeAnnotations not preserved!"
+set -x
+cp $TYPEANN2_JAVA $TEST_TMPDIR/TypeAnnotationTest2.java
+$JAVAC -J-Xbootclasspath/p:$LANGTOOLS8 $TEST_TMPDIR/TypeAnnotationTest2.java -cp $TYPEANN2_IJAR ||
+ die "javac failed"
+set +x
+
+# Check that ijar works on classes with invokedynamic
+$IJAR $INVOKEDYNAMIC_JAR $INVOKEDYNAMIC_IJAR || die "ijar failed"
+lines=$($JAVAP -c -private -classpath $INVOKEDYNAMIC_JAR ClassWithLambda | grep Code: | wc -l)
+check_eq 4 $lines "Input jar should have 4 method bodies!"
+lines=$($JAVAP -c -private -classpath $INVOKEDYNAMIC_IJAR ClassWithLambda | grep Code: | wc -l)
+check_eq 0 $lines "Interface jar should have no method bodies!"
+
+# Check that Object.class can be processed
+$IJAR $OBJECT_JAR $OBJECT_IJAR || die "ijar failed"
+
+# Check that the tool detects and reports a corrupted end of central directory
+# record condition
+CORRUPTED_JAR=$TEST_TMPDIR/corrupted.jar
+# First make the jar one byte longer
+cp $JAR_WRONG_CENTRAL_DIR $CORRUPTED_JAR
+chmod +w $CORRUPTED_JAR
+echo >> $CORRUPTED_JAR
+set +e
+capture_test_stderr
+$IJAR $CORRUPTED_JAR && die "ijar should have failed"
+status=$?
+set -e
+check_ne 0 $status
+grep_test_stderr "missing end of central directory record"
+restore_outputs
+# Then make the jar one byte shorter than the original one
+let "NEW_SIZE = `statfmt $CORRUPTED_JAR` - 2"
+set_file_length $CORRUPTED_JAR $NEW_SIZE
+set +e
+capture_test_stderr
+$IJAR $CORRUPTED_JAR && die "ijar should have failed"
+status=$?
+set -e
+check_ne 0 $status
+grep_test_stderr "missing end of central directory record"
+restore_outputs
+
+echo "PASS"